summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaymond <siuchow@google.com>2015-04-02 10:43:13 -0700
committerRaymond <siuchow@google.com>2015-04-02 10:43:13 -0700
commitdee0849a9704d532af0b550146cbafbaa6ee1d19 (patch)
tree8ccce3a046c214fb609977b7fc53c40cef7f9ea5
parent55b0a5efc929efa9615babd3e760547f94e3518e (diff)
downloadapache-commons-math-dee0849a9704d532af0b550146cbafbaa6ee1d19.tar.gz
third party library: apache-commons-mathandroid-cts-6.0_r9android-cts-6.0_r8android-cts-6.0_r7android-cts-6.0_r6android-cts-6.0_r5android-cts-6.0_r4android-cts-6.0_r32android-cts-6.0_r31android-cts-6.0_r30android-cts-6.0_r3android-cts-6.0_r29android-cts-6.0_r28android-cts-6.0_r27android-cts-6.0_r26android-cts-6.0_r25android-cts-6.0_r24android-cts-6.0_r23android-cts-6.0_r22android-cts-6.0_r21android-cts-6.0_r20android-cts-6.0_r2android-cts-6.0_r19android-cts-6.0_r18android-cts-6.0_r17android-cts-6.0_r16android-cts-6.0_r15android-cts-6.0_r14android-cts-6.0_r13android-cts-6.0_r12android-cts-6.0_r1android-6.0.1_r9android-6.0.1_r81android-6.0.1_r80android-6.0.1_r8android-6.0.1_r79android-6.0.1_r78android-6.0.1_r77android-6.0.1_r74android-6.0.1_r73android-6.0.1_r72android-6.0.1_r70android-6.0.1_r7android-6.0.1_r69android-6.0.1_r68android-6.0.1_r67android-6.0.1_r66android-6.0.1_r65android-6.0.1_r63android-6.0.1_r62android-6.0.1_r61android-6.0.1_r60android-6.0.1_r59android-6.0.1_r58android-6.0.1_r57android-6.0.1_r56android-6.0.1_r55android-6.0.1_r54android-6.0.1_r53android-6.0.1_r52android-6.0.1_r51android-6.0.1_r50android-6.0.1_r5android-6.0.1_r49android-6.0.1_r48android-6.0.1_r47android-6.0.1_r46android-6.0.1_r45android-6.0.1_r43android-6.0.1_r42android-6.0.1_r41android-6.0.1_r40android-6.0.1_r4android-6.0.1_r33android-6.0.1_r32android-6.0.1_r31android-6.0.1_r30android-6.0.1_r3android-6.0.1_r28android-6.0.1_r27android-6.0.1_r26android-6.0.1_r25android-6.0.1_r24android-6.0.1_r22android-6.0.1_r21android-6.0.1_r20android-6.0.1_r18android-6.0.1_r17android-6.0.1_r16android-6.0.1_r13android-6.0.1_r12android-6.0.1_r11android-6.0.1_r10android-6.0.1_r1android-6.0.0_r7android-6.0.0_r6android-6.0.0_r5android-6.0.0_r41android-6.0.0_r4android-6.0.0_r3android-6.0.0_r26android-6.0.0_r25android-6.0.0_r24android-6.0.0_r23android-6.0.0_r2android-6.0.0_r13android-6.0.0_r12android-6.0.0_r11android-6.0.0_r1marshmallow-releasemarshmallow-mr3-releasemarshmallow-mr2-releasemarshmallow-mr1-releasemarshmallow-mr1-devmarshmallow-dr1.6-releasemarshmallow-dr1.5-releasemarshmallow-dr1.5-devmarshmallow-dr-releasemarshmallow-dr-dragon-releasemarshmallow-dr-devmarshmallow-devmarshmallow-cts-release
Change-Id: I52a325624a7f0dd652b362a9840626d6d9f3c42b
-rw-r--r--Android.mk26
-rw-r--r--LICENSE376
-rw-r--r--NOTICE56
-rw-r--r--src/main/java/org/apache/commons/math/ArgumentOutsideDomainException.java44
-rw-r--r--src/main/java/org/apache/commons/math/ConvergenceException.java99
-rw-r--r--src/main/java/org/apache/commons/math/ConvergingAlgorithm.java141
-rw-r--r--src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java155
-rw-r--r--src/main/java/org/apache/commons/math/DimensionMismatchException.java67
-rw-r--r--src/main/java/org/apache/commons/math/DuplicateSampleAbscissaException.java51
-rw-r--r--src/main/java/org/apache/commons/math/Field.java51
-rw-r--r--src/main/java/org/apache/commons/math/FieldElement.java60
-rw-r--r--src/main/java/org/apache/commons/math/FunctionEvaluationException.java211
-rw-r--r--src/main/java/org/apache/commons/math/MathConfigurationException.java94
-rw-r--r--src/main/java/org/apache/commons/math/MathException.java217
-rw-r--r--src/main/java/org/apache/commons/math/MathRuntimeException.java717
-rw-r--r--src/main/java/org/apache/commons/math/MaxEvaluationsExceededException.java84
-rw-r--r--src/main/java/org/apache/commons/math/MaxIterationsExceededException.java84
-rw-r--r--src/main/java/org/apache/commons/math/analysis/BinaryFunction.java120
-rw-r--r--src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math/analysis/ComposableFunction.java506
-rw-r--r--src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java51
-rw-r--r--src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java36
-rw-r--r--src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java35
-rw-r--r--src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.java34
-rw-r--r--src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java35
-rw-r--r--src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java39
-rw-r--r--src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java39
-rw-r--r--src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java37
-rw-r--r--src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java36
-rw-r--r--src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java37
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java236
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java121
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java112
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java142
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java106
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java183
-rw-r--r--src/main/java/org/apache/commons/math/analysis/integration/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java558
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java145
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.java44
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java117
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java75
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java463
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java244
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.java118
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java46
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java54
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java178
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java143
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/SplineInterpolator.java127
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java483
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.java198
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.java49
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java39
-rw-r--r--src/main/java/org/apache/commons/math/analysis/interpolation/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/analysis/package.html33
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java350
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java305
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java221
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java224
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java281
-rw-r--r--src/main/java/org/apache/commons/math/analysis/polynomials/package.html23
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java133
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java399
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java434
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java415
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java183
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java247
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java230
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java165
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java90
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java64
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java304
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java240
-rw-r--r--src/main/java/org/apache/commons/math/analysis/solvers/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/complex/Complex.java1007
-rw-r--r--src/main/java/org/apache/commons/math/complex/ComplexField.java78
-rw-r--r--src/main/java/org/apache/commons/math/complex/ComplexFormat.java383
-rw-r--r--src/main/java/org/apache/commons/math/complex/ComplexUtils.java72
-rw-r--r--src/main/java/org/apache/commons/math/complex/package.html23
-rw-r--r--src/main/java/org/apache/commons/math/dfp/Dfp.java2399
-rw-r--r--src/main/java/org/apache/commons/math/dfp/DfpDec.java369
-rw-r--r--src/main/java/org/apache/commons/math/dfp/DfpField.java750
-rw-r--r--src/main/java/org/apache/commons/math/dfp/DfpMath.java969
-rw-r--r--src/main/java/org/apache/commons/math/dfp/package.html88
-rw-r--r--src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java231
-rw-r--r--src/main/java/org/apache/commons/math/distribution/AbstractDistribution.java69
-rw-r--r--src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java319
-rw-r--r--src/main/java/org/apache/commons/math/distribution/BetaDistribution.java65
-rw-r--r--src/main/java/org/apache/commons/math/distribution/BetaDistributionImpl.java284
-rw-r--r--src/main/java/org/apache/commons/math/distribution/BinomialDistribution.java60
-rw-r--r--src/main/java/org/apache/commons/math/distribution/BinomialDistributionImpl.java279
-rw-r--r--src/main/java/org/apache/commons/math/distribution/CauchyDistribution.java63
-rw-r--r--src/main/java/org/apache/commons/math/distribution/CauchyDistributionImpl.java320
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java53
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java324
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ContinuousDistribution.java43
-rw-r--r--src/main/java/org/apache/commons/math/distribution/DiscreteDistribution.java35
-rw-r--r--src/main/java/org/apache/commons/math/distribution/Distribution.java56
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ExponentialDistribution.java53
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java319
-rw-r--r--src/main/java/org/apache/commons/math/distribution/FDistribution.java60
-rw-r--r--src/main/java/org/apache/commons/math/distribution/FDistributionImpl.java360
-rw-r--r--src/main/java/org/apache/commons/math/distribution/GammaDistribution.java67
-rw-r--r--src/main/java/org/apache/commons/math/distribution/GammaDistributionImpl.java355
-rw-r--r--src/main/java/org/apache/commons/math/distribution/HasDensity.java45
-rw-r--r--src/main/java/org/apache/commons/math/distribution/HypergeometricDistribution.java76
-rw-r--r--src/main/java/org/apache/commons/math/distribution/HypergeometricDistributionImpl.java421
-rw-r--r--src/main/java/org/apache/commons/math/distribution/IntegerDistribution.java84
-rw-r--r--src/main/java/org/apache/commons/math/distribution/NormalDistribution.java65
-rw-r--r--src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java349
-rw-r--r--src/main/java/org/apache/commons/math/distribution/PascalDistribution.java73
-rw-r--r--src/main/java/org/apache/commons/math/distribution/PascalDistributionImpl.java276
-rw-r--r--src/main/java/org/apache/commons/math/distribution/PoissonDistribution.java64
-rw-r--r--src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java343
-rw-r--r--src/main/java/org/apache/commons/math/distribution/SaddlePointExpansion.java201
-rw-r--r--src/main/java/org/apache/commons/math/distribution/TDistribution.java46
-rw-r--r--src/main/java/org/apache/commons/math/distribution/TDistributionImpl.java303
-rw-r--r--src/main/java/org/apache/commons/math/distribution/WeibullDistribution.java66
-rw-r--r--src/main/java/org/apache/commons/math/distribution/WeibullDistributionImpl.java378
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ZipfDistribution.java71
-rw-r--r--src/main/java/org/apache/commons/math/distribution/ZipfDistributionImpl.java286
-rw-r--r--src/main/java/org/apache/commons/math/distribution/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/estimation/AbstractEstimator.java318
-rw-r--r--src/main/java/org/apache/commons/math/estimation/EstimatedParameter.java126
-rw-r--r--src/main/java/org/apache/commons/math/estimation/EstimationException.java61
-rw-r--r--src/main/java/org/apache/commons/math/estimation/EstimationProblem.java68
-rw-r--r--src/main/java/org/apache/commons/math/estimation/Estimator.java90
-rw-r--r--src/main/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java231
-rw-r--r--src/main/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java897
-rw-r--r--src/main/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java111
-rw-r--r--src/main/java/org/apache/commons/math/estimation/WeightedMeasurement.java172
-rw-r--r--src/main/java/org/apache/commons/math/estimation/package.html25
-rw-r--r--src/main/java/org/apache/commons/math/exception/ConvergenceException.java61
-rw-r--r--src/main/java/org/apache/commons/math/exception/DimensionMismatchException.java53
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathIllegalArgumentException.java112
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathIllegalNumberException.java74
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathIllegalStateException.java140
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathInternalError.java50
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathThrowable.java62
-rw-r--r--src/main/java/org/apache/commons/math/exception/MathUnsupportedOperationException.java106
-rw-r--r--src/main/java/org/apache/commons/math/exception/NoDataException.java47
-rw-r--r--src/main/java/org/apache/commons/math/exception/NonMonotonousSequenceException.java123
-rw-r--r--src/main/java/org/apache/commons/math/exception/NotPositiveException.java50
-rw-r--r--src/main/java/org/apache/commons/math/exception/NotStrictlyPositiveException.java50
-rw-r--r--src/main/java/org/apache/commons/math/exception/NullArgumentException.java50
-rw-r--r--src/main/java/org/apache/commons/math/exception/NumberIsTooLargeException.java89
-rw-r--r--src/main/java/org/apache/commons/math/exception/NumberIsTooSmallException.java90
-rw-r--r--src/main/java/org/apache/commons/math/exception/OutOfRangeException.java63
-rw-r--r--src/main/java/org/apache/commons/math/exception/ZeroException.java48
-rw-r--r--src/main/java/org/apache/commons/math/exception/package.html23
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/ArgUtils.java58
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/DummyLocalizable.java58
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/Localizable.java43
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java345
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/MessageFactory.java78
-rw-r--r--src/main/java/org/apache/commons/math/exception/util/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/fraction/AbstractFormat.java210
-rw-r--r--src/main/java/org/apache/commons/math/fraction/BigFraction.java1129
-rw-r--r--src/main/java/org/apache/commons/math/fraction/BigFractionField.java78
-rw-r--r--src/main/java/org/apache/commons/math/fraction/BigFractionFormat.java291
-rw-r--r--src/main/java/org/apache/commons/math/fraction/Fraction.java655
-rw-r--r--src/main/java/org/apache/commons/math/fraction/FractionConversionException.java56
-rw-r--r--src/main/java/org/apache/commons/math/fraction/FractionField.java78
-rw-r--r--src/main/java/org/apache/commons/math/fraction/FractionFormat.java274
-rw-r--r--src/main/java/org/apache/commons/math/fraction/ProperBigFractionFormat.java239
-rw-r--r--src/main/java/org/apache/commons/math/fraction/ProperFractionFormat.java232
-rw-r--r--src/main/java/org/apache/commons/math/fraction/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/genetics/AbstractListChromosome.java104
-rw-r--r--src/main/java/org/apache/commons/math/genetics/BinaryChromosome.java92
-rw-r--r--src/main/java/org/apache/commons/math/genetics/BinaryMutation.java52
-rw-r--r--src/main/java/org/apache/commons/math/genetics/Chromosome.java111
-rw-r--r--src/main/java/org/apache/commons/math/genetics/ChromosomePair.java69
-rw-r--r--src/main/java/org/apache/commons/math/genetics/CrossoverPolicy.java35
-rw-r--r--src/main/java/org/apache/commons/math/genetics/ElitisticListPopulation.java110
-rw-r--r--src/main/java/org/apache/commons/math/genetics/Fitness.java35
-rw-r--r--src/main/java/org/apache/commons/math/genetics/FixedGenerationCount.java70
-rw-r--r--src/main/java/org/apache/commons/math/genetics/GeneticAlgorithm.java228
-rw-r--r--src/main/java/org/apache/commons/math/genetics/InvalidRepresentationException.java63
-rw-r--r--src/main/java/org/apache/commons/math/genetics/ListPopulation.java155
-rw-r--r--src/main/java/org/apache/commons/math/genetics/MutationPolicy.java33
-rw-r--r--src/main/java/org/apache/commons/math/genetics/OnePointCrossover.java117
-rw-r--r--src/main/java/org/apache/commons/math/genetics/PermutationChromosome.java44
-rw-r--r--src/main/java/org/apache/commons/math/genetics/Population.java55
-rw-r--r--src/main/java/org/apache/commons/math/genetics/RandomKey.java290
-rw-r--r--src/main/java/org/apache/commons/math/genetics/RandomKeyMutation.java57
-rw-r--r--src/main/java/org/apache/commons/math/genetics/SelectionPolicy.java32
-rw-r--r--src/main/java/org/apache/commons/math/genetics/StoppingCondition.java35
-rw-r--r--src/main/java/org/apache/commons/math/genetics/TournamentSelection.java114
-rw-r--r--src/main/java/org/apache/commons/math/genetics/package.html24
-rw-r--r--src/main/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java45
-rw-r--r--src/main/java/org/apache/commons/math/geometry/NotARotationMatrixException.java60
-rw-r--r--src/main/java/org/apache/commons/math/geometry/Rotation.java1072
-rw-r--r--src/main/java/org/apache/commons/math/geometry/RotationOrder.java175
-rw-r--r--src/main/java/org/apache/commons/math/geometry/Vector3D.java534
-rw-r--r--src/main/java/org/apache/commons/math/geometry/Vector3DFormat.java343
-rw-r--r--src/main/java/org/apache/commons/math/geometry/package.html24
-rw-r--r--src/main/java/org/apache/commons/math/linear/AbstractFieldMatrix.java1139
-rw-r--r--src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java1071
-rw-r--r--src/main/java/org/apache/commons/math/linear/AbstractRealVector.java932
-rw-r--r--src/main/java/org/apache/commons/math/linear/AnyMatrix.java48
-rw-r--r--src/main/java/org/apache/commons/math/linear/Array2DRowFieldMatrix.java613
-rw-r--r--src/main/java/org/apache/commons/math/linear/Array2DRowRealMatrix.java621
-rw-r--r--src/main/java/org/apache/commons/math/linear/ArrayFieldVector.java869
-rw-r--r--src/main/java/org/apache/commons/math/linear/ArrayRealVector.java1228
-rw-r--r--src/main/java/org/apache/commons/math/linear/BiDiagonalTransformer.java381
-rw-r--r--src/main/java/org/apache/commons/math/linear/BigMatrix.java331
-rw-r--r--src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java1505
-rw-r--r--src/main/java/org/apache/commons/math/linear/BlockFieldMatrix.java1670
-rw-r--r--src/main/java/org/apache/commons/math/linear/BlockRealMatrix.java1690
-rw-r--r--src/main/java/org/apache/commons/math/linear/CholeskyDecomposition.java71
-rw-r--r--src/main/java/org/apache/commons/math/linear/CholeskyDecompositionImpl.java356
-rw-r--r--src/main/java/org/apache/commons/math/linear/DecompositionSolver.java84
-rw-r--r--src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixChangingVisitor.java62
-rw-r--r--src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixPreservingVisitor.java62
-rw-r--r--src/main/java/org/apache/commons/math/linear/DefaultRealMatrixChangingVisitor.java49
-rw-r--r--src/main/java/org/apache/commons/math/linear/DefaultRealMatrixPreservingVisitor.java49
-rw-r--r--src/main/java/org/apache/commons/math/linear/EigenDecomposition.java137
-rw-r--r--src/main/java/org/apache/commons/math/linear/EigenDecompositionImpl.java619
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldDecompositionSolver.java86
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldLUDecomposition.java94
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldLUDecompositionImpl.java434
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldMatrix.java798
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldMatrixChangingVisitor.java63
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldMatrixPreservingVisitor.java62
-rw-r--r--src/main/java/org/apache/commons/math/linear/FieldVector.java358
-rw-r--r--src/main/java/org/apache/commons/math/linear/InvalidMatrixException.java67
-rw-r--r--src/main/java/org/apache/commons/math/linear/LUDecomposition.java92
-rw-r--r--src/main/java/org/apache/commons/math/linear/LUDecompositionImpl.java423
-rw-r--r--src/main/java/org/apache/commons/math/linear/MatrixIndexException.java55
-rw-r--r--src/main/java/org/apache/commons/math/linear/MatrixUtils.java957
-rw-r--r--src/main/java/org/apache/commons/math/linear/MatrixVisitorException.java52
-rw-r--r--src/main/java/org/apache/commons/math/linear/NonSquareMatrixException.java42
-rw-r--r--src/main/java/org/apache/commons/math/linear/NotPositiveDefiniteMatrixException.java43
-rw-r--r--src/main/java/org/apache/commons/math/linear/NotSymmetricMatrixException.java43
-rw-r--r--src/main/java/org/apache/commons/math/linear/OpenMapRealMatrix.java292
-rw-r--r--src/main/java/org/apache/commons/math/linear/OpenMapRealVector.java915
-rw-r--r--src/main/java/org/apache/commons/math/linear/QRDecomposition.java77
-rw-r--r--src/main/java/org/apache/commons/math/linear/QRDecompositionImpl.java453
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealMatrix.java871
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealMatrixChangingVisitor.java62
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealMatrixImpl.java629
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealMatrixPreservingVisitor.java61
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealVector.java1005
-rw-r--r--src/main/java/org/apache/commons/math/linear/RealVectorFormat.java341
-rw-r--r--src/main/java/org/apache/commons/math/linear/SingularMatrixException.java40
-rw-r--r--src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java147
-rw-r--r--src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java379
-rw-r--r--src/main/java/org/apache/commons/math/linear/SparseFieldMatrix.java190
-rw-r--r--src/main/java/org/apache/commons/math/linear/SparseFieldVector.java657
-rw-r--r--src/main/java/org/apache/commons/math/linear/SparseRealMatrix.java29
-rw-r--r--src/main/java/org/apache/commons/math/linear/SparseRealVector.java27
-rw-r--r--src/main/java/org/apache/commons/math/linear/TriDiagonalTransformer.java270
-rw-r--r--src/main/java/org/apache/commons/math/linear/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java440
-rw-r--r--src/main/java/org/apache/commons/math/ode/ContinuousOutputModel.java379
-rw-r--r--src/main/java/org/apache/commons/math/ode/DerivativeException.java62
-rw-r--r--src/main/java/org/apache/commons/math/ode/ExtendedFirstOrderDifferentialEquations.java66
-rw-r--r--src/main/java/org/apache/commons/math/ode/FirstOrderConverter.java119
-rw-r--r--src/main/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java66
-rw-r--r--src/main/java/org/apache/commons/math/ode/FirstOrderIntegrator.java61
-rw-r--r--src/main/java/org/apache/commons/math/ode/IntegratorException.java64
-rw-r--r--src/main/java/org/apache/commons/math/ode/MultistepIntegrator.java412
-rw-r--r--src/main/java/org/apache/commons/math/ode/ODEIntegrator.java138
-rw-r--r--src/main/java/org/apache/commons/math/ode/SecondOrderDifferentialEquations.java69
-rw-r--r--src/main/java/org/apache/commons/math/ode/SecondOrderIntegrator.java60
-rw-r--r--src/main/java/org/apache/commons/math/ode/events/CombinedEventsManager.java249
-rw-r--r--src/main/java/org/apache/commons/math/ode/events/EventException.java63
-rw-r--r--src/main/java/org/apache/commons/math/ode/events/EventHandler.java183
-rw-r--r--src/main/java/org/apache/commons/math/ode/events/EventState.java431
-rw-r--r--src/main/java/org/apache/commons/math/ode/events/package.html96
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/EventHandlerWithJacobians.java226
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java899
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/ODEWithJacobians.java54
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/ParameterizedODE.java48
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/StepHandlerWithJacobians.java97
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/StepInterpolatorWithJacobians.java188
-rw-r--r--src/main/java/org/apache/commons/math/ode/jacobians/package.html28
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java317
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/AdamsIntegrator.java131
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java414
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/AdamsNordsieckTransformer.java312
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java349
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java75
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java110
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java158
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.java214
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.java283
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java480
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java379
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/EulerIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.java91
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/GillIntegrator.java74
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.java125
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java963
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java401
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java132
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.java103
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.java68
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.java100
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java197
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java183
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.java116
-rw-r--r--src/main/java/org/apache/commons/math/ode/nonstiff/package.html25
-rw-r--r--src/main/java/org/apache/commons/math/ode/package.html167
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java519
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java101
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.java150
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java62
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolator.java291
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/StepHandler.java78
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/StepInterpolator.java132
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java161
-rw-r--r--src/main/java/org/apache/commons/math/ode/sampling/package.html60
-rw-r--r--src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateRealOptimizer.java112
-rw-r--r--src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateVectorialOptimizer.java114
-rw-r--r--src/main/java/org/apache/commons/math/optimization/GoalType.java35
-rw-r--r--src/main/java/org/apache/commons/math/optimization/LeastSquaresConverter.java193
-rw-r--r--src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizer.java228
-rw-r--r--src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizer.java238
-rw-r--r--src/main/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizer.java216
-rw-r--r--src/main/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizer.java318
-rw-r--r--src/main/java/org/apache/commons/math/optimization/MultivariateRealOptimizer.java101
-rw-r--r--src/main/java/org/apache/commons/math/optimization/OptimizationException.java68
-rw-r--r--src/main/java/org/apache/commons/math/optimization/RealConvergenceChecker.java55
-rw-r--r--src/main/java/org/apache/commons/math/optimization/RealPointValuePair.java88
-rw-r--r--src/main/java/org/apache/commons/math/optimization/SimpleRealPointChecker.java87
-rw-r--r--src/main/java/org/apache/commons/math/optimization/SimpleScalarValueChecker.java81
-rw-r--r--src/main/java/org/apache/commons/math/optimization/SimpleVectorialPointChecker.java90
-rw-r--r--src/main/java/org/apache/commons/math/optimization/SimpleVectorialValueChecker.java90
-rw-r--r--src/main/java/org/apache/commons/math/optimization/UnivariateRealOptimizer.java116
-rw-r--r--src/main/java/org/apache/commons/math/optimization/VectorialConvergenceChecker.java55
-rw-r--r--src/main/java/org/apache/commons/math/optimization/VectorialPointValuePair.java100
-rw-r--r--src/main/java/org/apache/commons/math/optimization/direct/DirectSearchOptimizer.java418
-rw-r--r--src/main/java/org/apache/commons/math/optimization/direct/MultiDirectional.java144
-rw-r--r--src/main/java/org/apache/commons/math/optimization/direct/NelderMead.java181
-rw-r--r--src/main/java/org/apache/commons/math/optimization/direct/PowellOptimizer.java298
-rw-r--r--src/main/java/org/apache/commons/math/optimization/direct/package.html24
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/CurveFitter.java197
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/GaussianDerivativeFunction.java104
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/GaussianFitter.java117
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/GaussianFunction.java159
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/GaussianParametersGuesser.java271
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/HarmonicCoefficientsGuesser.java300
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFitter.java133
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFunction.java80
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunction.java165
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/ParametricRealFunction.java50
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/PolynomialFitter.java108
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/WeightedObservedPoint.java75
-rw-r--r--src/main/java/org/apache/commons/math/optimization/fitting/package.html30
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/AbstractLeastSquaresOptimizer.java374
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/AbstractScalarDifferentiableOptimizer.java207
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/ConjugateGradientFormula.java49
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizer.java135
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java888
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizer.java294
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/Preconditioner.java52
-rw-r--r--src/main/java/org/apache/commons/math/optimization/general/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/AbstractLinearOptimizer.java130
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/LinearConstraint.java233
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/LinearObjectiveFunction.java147
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/LinearOptimizer.java89
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/NoFeasibleSolutionException.java41
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/Relationship.java67
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/SimplexSolver.java185
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java589
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/UnboundedSolutionException.java41
-rw-r--r--src/main/java/org/apache/commons/math/optimization/linear/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/optimization/package.html72
-rw-r--r--src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java275
-rw-r--r--src/main/java/org/apache/commons/math/optimization/univariate/BracketFinder.java295
-rw-r--r--src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java227
-rw-r--r--src/main/java/org/apache/commons/math/optimization/univariate/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/random/AbstractRandomGenerator.java272
-rw-r--r--src/main/java/org/apache/commons/math/random/AbstractWell.java186
-rw-r--r--src/main/java/org/apache/commons/math/random/BitsStreamGenerator.java153
-rw-r--r--src/main/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java304
-rw-r--r--src/main/java/org/apache/commons/math/random/EmpiricalDistribution.java133
-rw-r--r--src/main/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java479
-rw-r--r--src/main/java/org/apache/commons/math/random/GaussianRandomGenerator.java47
-rw-r--r--src/main/java/org/apache/commons/math/random/JDKRandomGenerator.java50
-rw-r--r--src/main/java/org/apache/commons/math/random/MersenneTwister.java259
-rw-r--r--src/main/java/org/apache/commons/math/random/NormalizedRandomGenerator.java38
-rw-r--r--src/main/java/org/apache/commons/math/random/RandomAdaptor.java198
-rw-r--r--src/main/java/org/apache/commons/math/random/RandomData.java272
-rw-r--r--src/main/java/org/apache/commons/math/random/RandomDataImpl.java966
-rw-r--r--src/main/java/org/apache/commons/math/random/RandomGenerator.java148
-rw-r--r--src/main/java/org/apache/commons/math/random/RandomVectorGenerator.java35
-rw-r--r--src/main/java/org/apache/commons/math/random/UncorrelatedRandomVectorGenerator.java93
-rw-r--r--src/main/java/org/apache/commons/math/random/UniformRandomGenerator.java61
-rw-r--r--src/main/java/org/apache/commons/math/random/UnitSphereRandomVectorGenerator.java84
-rw-r--r--src/main/java/org/apache/commons/math/random/ValueServer.java385
-rw-r--r--src/main/java/org/apache/commons/math/random/Well1024a.java106
-rw-r--r--src/main/java/org/apache/commons/math/random/Well19937a.java108
-rw-r--r--src/main/java/org/apache/commons/math/random/Well19937c.java115
-rw-r--r--src/main/java/org/apache/commons/math/random/Well44497a.java111
-rw-r--r--src/main/java/org/apache/commons/math/random/Well44497b.java119
-rw-r--r--src/main/java/org/apache/commons/math/random/Well512a.java107
-rw-r--r--src/main/java/org/apache/commons/math/random/package.html132
-rw-r--r--src/main/java/org/apache/commons/math/special/Beta.java202
-rw-r--r--src/main/java/org/apache/commons/math/special/Erf.java92
-rw-r--r--src/main/java/org/apache/commons/math/special/Gamma.java339
-rw-r--r--src/main/java/org/apache/commons/math/special/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/Frequency.java603
-rw-r--r--src/main/java/org/apache/commons/math/stat/StatUtils.java663
-rw-r--r--src/main/java/org/apache/commons/math/stat/clustering/Cluster.java74
-rw-r--r--src/main/java/org/apache/commons/math/stat/clustering/Clusterable.java46
-rw-r--r--src/main/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPoint.java120
-rw-r--r--src/main/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.java333
-rw-r--r--src/main/java/org/apache/commons/math/stat/clustering/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/correlation/Covariance.java274
-rw-r--r--src/main/java/org/apache/commons/math/stat/correlation/PearsonsCorrelation.java285
-rw-r--r--src/main/java/org/apache/commons/math/stat/correlation/SpearmansCorrelation.java172
-rw-r--r--src/main/java/org/apache/commons/math/stat/correlation/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java183
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java232
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.java416
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java721
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java637
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java120
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/StatisticalSummary.java65
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/StatisticalSummaryValues.java186
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java86
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java717
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java172
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java299
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java333
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java53
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/WeightedEvaluation.java49
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java160
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java142
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java205
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java222
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/Mean.java272
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java124
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/SemiVariance.java379
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java213
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java271
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java139
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/Variance.java610
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java152
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialMean.java103
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/moment/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/package.html41
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/rank/Max.java163
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/rank/Median.java55
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/rank/Min.java163
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java497
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/rank/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/summary/Product.java224
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/summary/Sum.java220
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java165
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java154
-rw-r--r--src/main/java/org/apache/commons/math/stat/descriptive/summary/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/ChiSquareTest.java222
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java424
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/OneWayAnova.java103
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java210
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/TTest.java771
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/TTestImpl.java1069
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/TestUtils.java436
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/UnknownDistributionChiSquareTest.java144
-rw-r--r--src/main/java/org/apache/commons/math/stat/inference/package.html23
-rw-r--r--src/main/java/org/apache/commons/math/stat/package.html20
-rw-r--r--src/main/java/org/apache/commons/math/stat/ranking/NaNStrategy.java49
-rw-r--r--src/main/java/org/apache/commons/math/stat/ranking/NaturalRanking.java464
-rw-r--r--src/main/java/org/apache/commons/math/stat/ranking/RankingAlgorithm.java41
-rw-r--r--src/main/java/org/apache/commons/math/stat/ranking/TiesStrategy.java55
-rw-r--r--src/main/java/org/apache/commons/math/stat/ranking/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java366
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java136
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java70
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.java233
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/SimpleRegression.java639
-rw-r--r--src/main/java/org/apache/commons/math/stat/regression/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/transform/FastCosineTransformer.java262
-rw-r--r--src/main/java/org/apache/commons/math/transform/FastFourierTransformer.java912
-rw-r--r--src/main/java/org/apache/commons/math/transform/FastHadamardTransformer.java252
-rw-r--r--src/main/java/org/apache/commons/math/transform/FastSineTransformer.java252
-rw-r--r--src/main/java/org/apache/commons/math/transform/RealTransformer.java80
-rw-r--r--src/main/java/org/apache/commons/math/transform/package.html22
-rw-r--r--src/main/java/org/apache/commons/math/util/BigReal.java292
-rw-r--r--src/main/java/org/apache/commons/math/util/BigRealField.java78
-rw-r--r--src/main/java/org/apache/commons/math/util/CompositeFormat.java220
-rw-r--r--src/main/java/org/apache/commons/math/util/ContinuedFraction.java208
-rw-r--r--src/main/java/org/apache/commons/math/util/DefaultTransformer.java80
-rw-r--r--src/main/java/org/apache/commons/math/util/DoubleArray.java104
-rw-r--r--src/main/java/org/apache/commons/math/util/FastMath.java4047
-rw-r--r--src/main/java/org/apache/commons/math/util/MathUtils.java2265
-rw-r--r--src/main/java/org/apache/commons/math/util/MultidimensionalCounter.java316
-rw-r--r--src/main/java/org/apache/commons/math/util/NumberTransformer.java39
-rw-r--r--src/main/java/org/apache/commons/math/util/OpenIntToDoubleHashMap.java600
-rw-r--r--src/main/java/org/apache/commons/math/util/OpenIntToFieldHashMap.java620
-rw-r--r--src/main/java/org/apache/commons/math/util/ResizableDoubleArray.java936
-rw-r--r--src/main/java/org/apache/commons/math/util/TransformerMap.java189
-rw-r--r--src/main/java/org/apache/commons/math/util/package.html20
501 files changed, 116343 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..df7b569
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+apache-commons-math_src_files := $(call all-java-files-under,src/main/java)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := apache-commons-math
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(apache-commons-math_src_files)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_SDK_VERSION := current
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..275e288
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,376 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+APACHE COMMONS MATH DERIVATIVE WORKS:
+
+The Apache commons-math library includes a number of subcomponents
+whose implementation is derived from original sources written
+in C or Fortran. License terms of the original sources
+are reproduced below.
+
+===============================================================================
+For the lmder, lmpar and qrsolv Fortran routine from minpack and translated in
+the LevenbergMarquardtOptimizer class in package
+org.apache.commons.math.optimization.general
+Original source copyright and license statement:
+
+Minpack Copyright Notice (1999) University of Chicago. All rights reserved
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above
+copyright notice, this list of conditions and the following
+disclaimer.
+
+2. 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.
+
+3. The end-user documentation included with the
+redistribution, if any, must include the following
+acknowledgment:
+
+ "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.
+
+4. 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.
+
+5. 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.
+===============================================================================
+
+Copyright and license statement for the odex Fortran routine developed by
+E. Hairer and G. Wanner and translated in GraggBulirschStoerIntegrator class
+in package org.apache.commons.math.ode.nonstiff:
+
+
+Copyright (c) 2004, Ernst Hairer
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- 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.
+
+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.
+===============================================================================
+
+Copyright and license statement for the original lapack fortran routines
+translated in EigenDecompositionImpl class in package
+org.apache.commons.math.linear:
+
+Copyright (c) 1992-2008 The University of Tennessee. All rights reserved.
+
+$COPYRIGHT$
+
+Additional copyrights may follow
+
+$HEADER$
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer listed
+ in this license in the documentation and/or other materials
+ provided with the distribution.
+
+- Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+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.
+===============================================================================
+
+Copyright and license statement for the original Mersenne twister C
+routines translated in MersenneTwister class in package
+org.apache.commons.math.random:
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ 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.
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..977df71
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,56 @@
+Apache Commons Math
+Copyright 2001-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+===============================================================================
+
+The BracketFinder (package org.apache.commons.math.optimization.univariate)
+and PowellOptimizer (package org.apache.commons.math.optimization.general)
+classes are based on the Python code in module "optimize.py" (version 0.5)
+developed by Travis E. Oliphant for the SciPy library (http://www.scipy.org/)
+Copyright © 2003-2009 SciPy Developers.
+===============================================================================
+
+The LinearConstraint, LinearObjectiveFunction, LinearOptimizer,
+RelationShip, SimplexSolver and SimplexTableau classes in package
+org.apache.commons.math.optimization.linear include software developed by
+Benjamin McCann (http://www.benmccann.com) and distributed with
+the following copyright: Copyright 2009 Google Inc.
+===============================================================================
+
+This product includes software developed by the
+University of Chicago, as Operator of Argonne National
+Laboratory.
+The LevenbergMarquardtOptimizer class in package
+org.apache.commons.math.optimization.general includes software
+translated from the lmder, lmpar and qrsolv Fortran routines
+from the Minpack package
+Minpack Copyright Notice (1999) University of Chicago. All rights reserved
+===============================================================================
+
+The GraggBulirschStoerIntegrator class in package
+org.apache.commons.math.ode.nonstiff includes software translated
+from the odex Fortran routine developed by E. Hairer and G. Wanner.
+Original source copyright:
+Copyright (c) 2004, Ernst Hairer
+===============================================================================
+
+The EigenDecompositionImpl class in package
+org.apache.commons.math.linear includes software translated
+from some LAPACK Fortran routines. Original source copyright:
+Copyright (c) 1992-2008 The University of Tennessee. All rights reserved.
+===============================================================================
+
+The MersenneTwister class in package org.apache.commons.math.random
+includes software translated from the 2002-01-26 version of
+the Mersenne-Twister generator written in C by Makoto Matsumoto and Takuji
+Nishimura. Original source copyright:
+Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+All rights reserved
+===============================================================================
+
+The complete text of licenses and disclaimers associated with the the original
+sources enumerated above at the time of code translation are in the LICENSE.txt
+file.
diff --git a/src/main/java/org/apache/commons/math/ArgumentOutsideDomainException.java b/src/main/java/org/apache/commons/math/ArgumentOutsideDomainException.java
new file mode 100644
index 0000000..a43c6b9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ArgumentOutsideDomainException.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.math;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a method is called with an out of bounds argument.
+ *
+ * @since 1.2
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class ArgumentOutsideDomainException extends FunctionEvaluationException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4965972841162580234L;
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param argument the failing function argument
+ * @param lower lower bound of the domain
+ * @param upper upper bound of the domain
+ */
+ public ArgumentOutsideDomainException(double argument, double lower, double upper) {
+ super(argument, LocalizedFormats.ARGUMENT_OUTSIDE_DOMAIN, argument, lower, upper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ConvergenceException.java b/src/main/java/org/apache/commons/math/ConvergenceException.java
new file mode 100644
index 0000000..0cc3959
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ConvergenceException.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math;
+
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a numerical computation can not be performed because the
+ * numerical result failed to converge to a finite value.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class ConvergenceException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1111352570797662604L;
+
+ /**
+ * Default constructor.
+ */
+ public ConvergenceException() {
+ super(LocalizedFormats.CONVERGENCE_FAILED);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ * @deprecated as of 2.2 replaced by {@link #ConvergenceException(Localizable, Object...)}
+ */
+ @Deprecated
+ public ConvergenceException(String pattern, Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public ConvergenceException(Localizable pattern, Object ... arguments) {
+ super(pattern, arguments);
+ }
+
+ /**
+ * Create an exception with a given root cause.
+ * @param cause the exception or error that caused this exception to be thrown
+ */
+ public ConvergenceException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ * @deprecated as of 2.2 replaced by {@link #ConvergenceException(Throwable, Localizable, Object...)}
+ */
+ @Deprecated
+ public ConvergenceException(Throwable cause, String pattern, Object ... arguments) {
+ this(cause, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public ConvergenceException(Throwable cause, Localizable pattern, Object ... arguments) {
+ super(cause, pattern, arguments);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ConvergingAlgorithm.java b/src/main/java/org/apache/commons/math/ConvergingAlgorithm.java
new file mode 100644
index 0000000..128f169
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ConvergingAlgorithm.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.math;
+
+
+/**
+ * Interface for algorithms handling convergence settings.
+ * <p>
+ * This interface only deals with convergence parameters setting, not
+ * execution of the algorithms per se.
+ * </p>
+ * @see ConvergenceException
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ * @since 2.0
+ * @deprecated in 2.2 (to be removed in 3.0). The concept of "iteration" will
+ * be moved to a new {@code IterativeAlgorithm}. The concept of "accuracy" is
+ * currently is also contained in {@link org.apache.commons.math.optimization.SimpleRealPointChecker}
+ * and similar classes.
+ */
+@Deprecated
+public interface ConvergingAlgorithm {
+
+ /**
+ * Set the upper limit for the number of iterations.
+ * <p>
+ * 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.</p>
+ * <p>
+ * A {@link ConvergenceException} will be thrown if this number
+ * is exceeded.</p>
+ *
+ * @param count maximum number of iterations
+ */
+ void setMaximalIterationCount(int count);
+
+ /**
+ * Get the upper limit for the number of iterations.
+ *
+ * @return the actual upper limit
+ */
+ int getMaximalIterationCount();
+
+ /**
+ * Reset the upper limit for the number of iterations to the default.
+ * <p>
+ * The default value is supplied by the algorithm implementation.</p>
+ *
+ * @see #setMaximalIterationCount(int)
+ */
+ void resetMaximalIterationCount();
+
+ /**
+ * Set the absolute accuracy.
+ * <p>
+ * 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.</p>
+ * <p>
+ * Algorithms are advised to do a plausibility check with the relative
+ * accuracy, but clients should not rely on this.</p>
+ *
+ * @param accuracy the accuracy.
+ * @throws IllegalArgumentException if the accuracy can't be achieved by
+ * the solver or is otherwise deemed unreasonable.
+ */
+ void setAbsoluteAccuracy(double accuracy);
+
+ /**
+ * Get the actual absolute accuracy.
+ *
+ * @return the accuracy
+ */
+ double getAbsoluteAccuracy();
+
+ /**
+ * Reset the absolute accuracy to the default.
+ * <p>
+ * The default value is provided by the algorithm implementation.</p>
+ */
+ void resetAbsoluteAccuracy();
+
+ /**
+ * Set the relative accuracy.
+ * <p>
+ * This is used to stop iterations if the absolute accuracy can't be
+ * achieved due to large values or short mantissa length.</p>
+ * <p>
+ * 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.math.util.MathUtils#SAFE_MIN MathUtils.SAFE_MIN}.</p>
+ *
+ * @param accuracy the relative accuracy.
+ * @throws IllegalArgumentException if the accuracy can't be achieved by
+ * the algorithm or is otherwise deemed unreasonable.
+ */
+ void setRelativeAccuracy(double accuracy);
+
+ /**
+ * Get the actual relative accuracy.
+ * @return the accuracy
+ */
+ double getRelativeAccuracy();
+
+ /**
+ * Reset the relative accuracy to the default.
+ * The default value is provided by the algorithm implementation.
+ */
+ void resetRelativeAccuracy();
+
+ /**
+ * Get the number of iterations in the last run of the algorithm.
+ * <p>
+ * This is mainly meant for testing purposes. It may occasionally
+ * help track down performance problems: if the iteration count
+ * is notoriously high, check whether the problem is evaluated
+ * properly, and whether another algorithm is more amenable to the
+ * problem.</p>
+ *
+ * @return the last iteration count.
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed.
+ */
+ int getIterationCount();
+
+}
diff --git a/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java b/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.java
new file mode 100644
index 0000000..e15b9a9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ConvergingAlgorithmImpl.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.math;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * converging algorithms.
+ *
+ * @version $Revision: 1062691 $ $Date: 2011-01-24 10:12:47 +0100 (lun. 24 janv. 2011) $
+ * @since 2.0
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+public abstract class ConvergingAlgorithmImpl implements ConvergingAlgorithm {
+
+ /** Maximum absolute error. */
+ protected double absoluteAccuracy;
+
+ /** Maximum relative error. */
+ protected double relativeAccuracy;
+
+ /** Maximum number of iterations. */
+ protected int maximalIterationCount;
+
+ /** Default maximum absolute error. */
+ protected double defaultAbsoluteAccuracy;
+
+ /** Default maximum relative error. */
+ protected double defaultRelativeAccuracy;
+
+ /** Default maximum number of iterations. */
+ protected int defaultMaximalIterationCount;
+
+ /** The last iteration count. */
+ protected int iterationCount;
+
+ /**
+ * Construct an algorithm with given iteration count and accuracy.
+ *
+ * @param defaultAbsoluteAccuracy maximum absolute error
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the
+ * defaultAbsoluteAccuracy is not valid
+ * @deprecated in 2.2. Derived classes should use the "setter" methods
+ * in order to assign meaningful values to all the instances variables.
+ */
+ @Deprecated
+ protected ConvergingAlgorithmImpl(final int defaultMaximalIterationCount,
+ final double defaultAbsoluteAccuracy) {
+ this.defaultAbsoluteAccuracy = defaultAbsoluteAccuracy;
+ this.defaultRelativeAccuracy = 1.0e-14;
+ this.absoluteAccuracy = defaultAbsoluteAccuracy;
+ this.relativeAccuracy = defaultRelativeAccuracy;
+ this.defaultMaximalIterationCount = defaultMaximalIterationCount;
+ this.maximalIterationCount = defaultMaximalIterationCount;
+ this.iterationCount = 0;
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @since 2.2
+ * @deprecated in 2.2 (to be removed as soon as the single non-default one
+ * has been removed).
+ */
+ @Deprecated
+ protected ConvergingAlgorithmImpl() {}
+
+ /** {@inheritDoc} */
+ public int getIterationCount() {
+ return iterationCount;
+ }
+
+ /** {@inheritDoc} */
+ public void setAbsoluteAccuracy(double accuracy) {
+ absoluteAccuracy = accuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double getAbsoluteAccuracy() {
+ return absoluteAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public void resetAbsoluteAccuracy() {
+ absoluteAccuracy = defaultAbsoluteAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaximalIterationCount(int count) {
+ maximalIterationCount = count;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaximalIterationCount() {
+ return maximalIterationCount;
+ }
+
+ /** {@inheritDoc} */
+ public void resetMaximalIterationCount() {
+ maximalIterationCount = defaultMaximalIterationCount;
+ }
+
+ /** {@inheritDoc} */
+ public void setRelativeAccuracy(double accuracy) {
+ relativeAccuracy = accuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double getRelativeAccuracy() {
+ return relativeAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public void resetRelativeAccuracy() {
+ relativeAccuracy = defaultRelativeAccuracy;
+ }
+
+ /**
+ * Reset the iterations counter to 0.
+ *
+ * @since 2.2
+ */
+ protected void resetIterationsCounter() {
+ iterationCount = 0;
+ }
+
+ /**
+ * Increment the iterations counter by 1.
+ *
+ * @throws MaxIterationsExceededException if the maximal number
+ * of iterations is exceeded.
+ * @since 2.2
+ */
+ protected void incrementIterationsCounter()
+ throws MaxIterationsExceededException {
+ if (++iterationCount > maximalIterationCount) {
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/DimensionMismatchException.java b/src/main/java/org/apache/commons/math/DimensionMismatchException.java
new file mode 100644
index 0000000..b1c3dc4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/DimensionMismatchException.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.math;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when two dimensions differ.
+ *
+ * @since 1.2
+ * @version $Revision: 1061778 $ $Date: 2011-01-21 13:12:39 +0100 (ven. 21 janv. 2011) $
+ * @deprecated in 2.2 (to be removed in 3.0). Please use its equivalent from package
+ * {@link org.apache.commons.math.exception}.
+ */
+public class DimensionMismatchException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1316089546353786411L;
+
+ /** First dimension. */
+ private final int dimension1;
+
+ /** Second dimension. */
+ private final int dimension2;
+
+ /**
+ * Construct an exception from the mismatched dimensions
+ * @param dimension1 first dimension
+ * @param dimension2 second dimension
+ */
+ public DimensionMismatchException(final int dimension1, final int dimension2) {
+ super(LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, dimension1, dimension2);
+ this.dimension1 = dimension1;
+ this.dimension2 = dimension2;
+ }
+
+ /**
+ * Get the first dimension
+ * @return first dimension
+ */
+ public int getDimension1() {
+ return dimension1;
+ }
+
+ /**
+ * Get the second dimension
+ * @return second dimension
+ */
+ public int getDimension2() {
+ return dimension2;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/DuplicateSampleAbscissaException.java b/src/main/java/org/apache/commons/math/DuplicateSampleAbscissaException.java
new file mode 100644
index 0000000..125be75
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/DuplicateSampleAbscissaException.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.math;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception thrown when a sample contains several entries at the same abscissa.
+ *
+ * @since 1.2
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class DuplicateSampleAbscissaException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2271007547170169872L;
+
+ /**
+ * Construct an exception indicating the duplicate abscissa.
+ * @param abscissa duplicate abscissa
+ * @param i1 index of one entry having the duplicate abscissa
+ * @param i2 index of another entry having the duplicate abscissa
+ */
+ public DuplicateSampleAbscissaException(double abscissa, int i1, int i2) {
+ super(LocalizedFormats.DUPLICATED_ABSCISSA,
+ abscissa, i1, i2);
+ }
+
+ /**
+ * Get the duplicate abscissa.
+ * @return duplicate abscissa
+ */
+ public double getDuplicateAbscissa() {
+ return ((Double) getArguments()[0]).doubleValue();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/Field.java b/src/main/java/org/apache/commons/math/Field.java
new file mode 100644
index 0000000..7e69a8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/Field.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.math;
+
+/**
+ * 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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @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();
+
+}
diff --git a/src/main/java/org/apache/commons/math/FieldElement.java b/src/main/java/org/apache/commons/math/FieldElement.java
new file mode 100644
index 0000000..29757d4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/FieldElement.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.math;
+
+
+/**
+ * Interface representing <a href="http://mathworld.wolfram.com/Field.html">field</a> elements.
+ * @param <T> the type of the field elements
+ * @see Field
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public interface FieldElement<T> {
+
+ /** Compute this + a.
+ * @param a element to add
+ * @return a new element representing this + a
+ */
+ T add(T a);
+
+ /** Compute this - a.
+ * @param a element to subtract
+ * @return a new element representing this - a
+ */
+ T subtract(T a);
+
+ /** Compute this &times; a.
+ * @param a element to multiply
+ * @return a new element representing this &times; a
+ */
+ T multiply(T a);
+
+ /** Compute this &divide; a.
+ * @param a element to add
+ * @return a new element representing this &divide; a
+ * @exception ArithmeticException if a is the zero of the
+ * additive operation (i.e. additive identity)
+ */
+ T divide(T a) throws ArithmeticException;
+
+ /** 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/math/FunctionEvaluationException.java b/src/main/java/org/apache/commons/math/FunctionEvaluationException.java
new file mode 100644
index 0000000..5d54d29
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/FunctionEvaluationException.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.math;
+
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.ArrayRealVector;
+
+/**
+ * Exception thrown when an error occurs evaluating a function.
+ * <p>
+ * Maintains an <code>argument</code> property holding the input value that
+ * caused the function evaluation to fail.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class FunctionEvaluationException extends MathException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1384427981840836868L;
+
+ /** Argument causing function evaluation failure */
+ private double[] argument;
+
+ /**
+ * Construct an exception indicating the argument value
+ * that caused the function evaluation to fail.
+ *
+ * @param argument the failing function argument
+ */
+ public FunctionEvaluationException(double argument) {
+ super(LocalizedFormats.EVALUATION_FAILED, argument);
+ this.argument = new double[] { argument };
+ }
+
+ /**
+ * Construct an exception indicating the argument value
+ * that caused the function evaluation to fail.
+ *
+ * @param argument the failing function argument
+ * @since 2.0
+ */
+ public FunctionEvaluationException(double[] argument) {
+ super(LocalizedFormats.EVALUATION_FAILED, new ArrayRealVector(argument));
+ this.argument = argument.clone();
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ */
+ public FunctionEvaluationException(double argument,
+ String pattern, Object ... arguments) {
+ this(argument, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public FunctionEvaluationException(double argument,
+ Localizable pattern, Object ... arguments) {
+ super(pattern, arguments);
+ this.argument = new double[] { argument };
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.0
+ */
+ public FunctionEvaluationException(double[] argument,
+ String pattern, Object ... arguments) {
+ this(argument, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public FunctionEvaluationException(double[] argument,
+ Localizable pattern, Object ... arguments) {
+ super(pattern, arguments);
+ this.argument = argument.clone();
+ }
+
+ /**
+ * Constructs an exception with specified root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @since 1.2
+ */
+ public FunctionEvaluationException(Throwable cause, double argument) {
+ super(cause);
+ this.argument = new double[] { argument };
+ }
+
+ /**
+ * Constructs an exception with specified root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @since 2.0
+ */
+ public FunctionEvaluationException(Throwable cause, double[] argument) {
+ super(cause);
+ this.argument = argument.clone();
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ */
+ public FunctionEvaluationException(Throwable cause,
+ double argument, String pattern,
+ Object ... arguments) {
+ this(cause, argument, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public FunctionEvaluationException(Throwable cause,
+ double argument, Localizable pattern,
+ Object ... arguments) {
+ super(cause, pattern, arguments);
+ this.argument = new double[] { argument };
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.0
+ */
+ public FunctionEvaluationException(Throwable cause,
+ double[] argument, String pattern,
+ Object ... arguments) {
+ this(cause, argument, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param argument the failing function argument
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public FunctionEvaluationException(Throwable cause,
+ double[] argument, Localizable pattern,
+ Object ... arguments) {
+ super(cause, pattern, arguments);
+ this.argument = argument.clone();
+ }
+
+ /**
+ * Returns the function argument that caused this exception.
+ *
+ * @return argument that caused function evaluation to fail
+ */
+ public double[] getArgument() {
+ return argument.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/MathConfigurationException.java b/src/main/java/org/apache/commons/math/MathConfigurationException.java
new file mode 100644
index 0000000..52a5b3e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/MathConfigurationException.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.math;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Signals a configuration problem with any of the factory methods.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class MathConfigurationException extends MathException implements Serializable{
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5261476508226103366L;
+
+ /**
+ * Default constructor.
+ */
+ public MathConfigurationException() {
+ super();
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ */
+ public MathConfigurationException(String pattern, Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathConfigurationException(Localizable pattern, Object ... arguments) {
+ super(pattern, arguments);
+ }
+
+ /**
+ * Create an exception with a given root cause.
+ * @param cause the exception or error that caused this exception to be thrown
+ */
+ public MathConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ */
+ public MathConfigurationException(Throwable cause, String pattern, Object ... arguments) {
+ this(cause, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message and root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param cause the exception or error that caused this exception to be thrown
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathConfigurationException(Throwable cause, Localizable pattern, Object ... arguments) {
+ super(cause, pattern, arguments);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/MathException.java b/src/main/java/org/apache/commons/math/MathException.java
new file mode 100644
index 0000000..5f1a566
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/MathException.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.math;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+import org.apache.commons.math.exception.MathThrowable;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+
+/**
+ * Base class for commons-math checked exceptions.
+ * <p>
+ * Supports nesting, emulating JDK 1.4 behavior if necessary.</p>
+ * <p>
+ * Adapted from <a href="http://commons.apache.org/collections/api-release/org/apache/commons/collections/FunctorException.html"/>.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class MathException extends Exception implements MathThrowable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 7428019509644517071L;
+
+ /**
+ * Pattern used to build the message.
+ */
+ private final Localizable pattern;
+
+ /**
+ * Arguments used to build the message.
+ */
+ private final Object[] arguments;
+
+ /**
+ * Constructs a new <code>MathException</code> with no
+ * detail message.
+ */
+ public MathException() {
+ this.pattern = LocalizedFormats.SIMPLE_MESSAGE;
+ this.arguments = new Object[] { "" };
+ }
+
+ /**
+ * Constructs a new <code>MathException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MathException(Localizable, Object...)}
+ */
+ @Deprecated
+ public MathException(String pattern, Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>MathException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathException(Localizable pattern, Object ... arguments) {
+ this.pattern = pattern;
+ this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
+ }
+
+ /**
+ * Constructs a new <code>MathException</code> with specified
+ * nested <code>Throwable</code> root cause.
+ *
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ */
+ public MathException(Throwable rootCause) {
+ super(rootCause);
+ this.pattern = LocalizedFormats.SIMPLE_MESSAGE;
+ this.arguments = new Object[] { (rootCause == null) ? "" : rootCause.getMessage() };
+ }
+
+ /**
+ * Constructs a new <code>MathException</code> with specified
+ * formatted detail message and nested <code>Throwable</code> root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 1.2
+ * @deprecated as of 2.2 replaced by {@link #MathException(Throwable, Localizable, Object...)}
+ */
+ @Deprecated
+ public MathException(Throwable rootCause, String pattern, Object ... arguments) {
+ this(rootCause, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>MathException</code> with specified
+ * formatted detail message and nested <code>Throwable</code> root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathException(Throwable rootCause, Localizable pattern, Object ... arguments) {
+ super(rootCause);
+ this.pattern = pattern;
+ this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
+ }
+
+ /** Gets the pattern used to build the message of this throwable.
+ *
+ * @return the pattern used to build the message of this throwable
+ * @since 1.2
+ * @deprecated as of 2.2 replaced by {@link #getSpecificPattern()} and {@link #getGeneralPattern()}
+ */
+ @Deprecated
+ public String getPattern() {
+ return pattern.getSourceString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.2
+ */
+ public Localizable getSpecificPattern() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.2
+ */
+ public Localizable getGeneralPattern() {
+ return pattern;
+ }
+
+ /** {@inheritDoc} */
+ public Object[] getArguments() {
+ return arguments.clone();
+ }
+
+ /** Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated
+ *
+ * @return localized message
+ * @since 1.2
+ */
+ public String getMessage(final Locale locale) {
+ if (pattern != null) {
+ return new MessageFormat(pattern.getLocalizedString(locale), locale).format(arguments);
+ }
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+
+ /**
+ * Prints the stack trace of this exception to the standard error stream.
+ */
+ @Override
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints the stack trace of this exception to the specified stream.
+ *
+ * @param out the <code>PrintStream</code> to use for output
+ */
+ @Override
+ public void printStackTrace(PrintStream out) {
+ synchronized (out) {
+ PrintWriter pw = new PrintWriter(out, false);
+ printStackTrace(pw);
+ // Flush the PrintWriter before it's GC'ed.
+ pw.flush();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/MathRuntimeException.java b/src/main/java/org/apache/commons/math/MathRuntimeException.java
new file mode 100644
index 0000000..3b9b89c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/MathRuntimeException.java
@@ -0,0 +1,717 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.ConcurrentModificationException;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.math.exception.MathThrowable;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+* Base class for commons-math unchecked exceptions.
+*
+* @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+* @since 2.0
+*/
+public class MathRuntimeException extends RuntimeException implements MathThrowable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 9058794795027570002L;
+
+ /**
+ * Pattern used to build the message.
+ */
+ private final Localizable pattern;
+
+ /**
+ * Arguments used to build the message.
+ */
+ private final Object[] arguments;
+
+ /**
+ * Constructs a new <code>MathRuntimeException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MathRuntimeException(Localizable, Object...)}
+ */
+ @Deprecated
+ public MathRuntimeException(final String pattern, final Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>MathRuntimeException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathRuntimeException(final Localizable pattern, final Object ... arguments) {
+ this.pattern = pattern;
+ this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
+ }
+
+ /**
+ * Constructs a new <code>MathRuntimeException</code> with specified
+ * nested <code>Throwable</code> root cause.
+ *
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ */
+ public MathRuntimeException(final Throwable rootCause) {
+ super(rootCause);
+ this.pattern = LocalizedFormats.SIMPLE_MESSAGE;
+ this.arguments = new Object[] { (rootCause == null) ? "" : rootCause.getMessage() };
+ }
+
+ /**
+ * Constructs a new <code>MathRuntimeException</code> with specified
+ * formatted detail message and nested <code>Throwable</code> root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MathRuntimeException(Throwable, Localizable, Object...)}
+ */
+ @Deprecated
+ public MathRuntimeException(final Throwable rootCause,
+ final String pattern, final Object ... arguments) {
+ this(rootCause, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>MathRuntimeException</code> with specified
+ * formatted detail message and nested <code>Throwable</code> root cause.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MathRuntimeException(final Throwable rootCause,
+ final Localizable pattern, final Object ... arguments) {
+ super(rootCause);
+ this.pattern = pattern;
+ this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
+ }
+
+ /**
+ * Builds a message string by from a pattern and its arguments.
+ * @param locale Locale in which the message should be translated
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return a message string
+ * @since 2.2
+ */
+ private static String buildMessage(final Locale locale, final Localizable pattern,
+ final Object ... arguments) {
+ return new MessageFormat(pattern.getLocalizedString(locale), locale).format(arguments);
+ }
+
+ /** Gets the pattern used to build the message of this throwable.
+ *
+ * @return the pattern used to build the message of this throwable
+ * @deprecated as of 2.2 replaced by {@link #getSpecificPattern()} and {@link #getGeneralPattern()}
+ */
+ @Deprecated
+ public String getPattern() {
+ return pattern.getSourceString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.2
+ */
+ public Localizable getSpecificPattern() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.2
+ */
+ public Localizable getGeneralPattern() {
+ return pattern;
+ }
+
+ /** {@inheritDoc} */
+ public Object[] getArguments() {
+ return arguments.clone();
+ }
+
+ /** Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated
+ *
+ * @return localized message
+ */
+ public String getMessage(final Locale locale) {
+ if (pattern != null) {
+ return buildMessage(locale, pattern, arguments);
+ }
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+
+ /**
+ * Prints the stack trace of this exception to the standard error stream.
+ */
+ @Override
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * Prints the stack trace of this exception to the specified stream.
+ *
+ * @param out the <code>PrintStream</code> to use for output
+ */
+ @Override
+ public void printStackTrace(final PrintStream out) {
+ synchronized (out) {
+ PrintWriter pw = new PrintWriter(out, false);
+ printStackTrace(pw);
+ // Flush the PrintWriter before it's GC'ed.
+ pw.flush();
+ }
+ }
+
+ /**
+ * Constructs a new <code>ArithmeticException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createArithmeticException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static ArithmeticException createArithmeticException(final String pattern,
+ final Object ... arguments) {
+ return createArithmeticException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>ArithmeticException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static ArithmeticException createArithmeticException(final Localizable pattern,
+ final Object ... arguments) {
+ return new ArithmeticException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 5305498554076846637L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>ArrayIndexOutOfBoundsException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createArrayIndexOutOfBoundsException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static ArrayIndexOutOfBoundsException createArrayIndexOutOfBoundsException(final String pattern,
+ final Object ... arguments) {
+ return createArrayIndexOutOfBoundsException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>ArrayIndexOutOfBoundsException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static ArrayIndexOutOfBoundsException createArrayIndexOutOfBoundsException(final Localizable pattern,
+ final Object ... arguments) {
+ return new ArrayIndexOutOfBoundsException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6718518191249632175L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>EOFException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createEOFException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static EOFException createEOFException(final String pattern,
+ final Object ... arguments) {
+ return createEOFException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>EOFException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static EOFException createEOFException(final Localizable pattern,
+ final Object ... arguments) {
+ return new EOFException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6067985859347601503L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>IOException</code> with specified nested
+ * <code>Throwable</code> root cause.
+ * <p>This factory method allows chaining of other exceptions within an
+ * <code>IOException</code> even for Java 5. The constructor for
+ * <code>IOException</code> with a cause parameter was introduced only
+ * with Java 6.</p>
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @return built exception
+ */
+ public static IOException createIOException(final Throwable rootCause) {
+ IOException ioe = new IOException(rootCause.getLocalizedMessage());
+ ioe.initCause(rootCause);
+ return ioe;
+ }
+
+ /**
+ * Constructs a new <code>IllegalArgumentException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createIllegalArgumentException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static IllegalArgumentException createIllegalArgumentException(final String pattern,
+ final Object ... arguments) {
+ return createIllegalArgumentException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>IllegalArgumentException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static IllegalArgumentException createIllegalArgumentException(final Localizable pattern,
+ final Object ... arguments) {
+ return new IllegalArgumentException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4284649691002411505L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>IllegalArgumentException</code> with specified nested
+ * <code>Throwable</code> root cause.
+ * @param rootCause the exception or error that caused this exception
+ * to be thrown.
+ * @return built exception
+ */
+ public static IllegalArgumentException createIllegalArgumentException(final Throwable rootCause) {
+ IllegalArgumentException iae = new IllegalArgumentException(rootCause.getLocalizedMessage());
+ iae.initCause(rootCause);
+ return iae;
+ }
+
+ /**
+ * Constructs a new <code>IllegalStateException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createIllegalStateException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static IllegalStateException createIllegalStateException(final String pattern,
+ final Object ... arguments) {
+ return createIllegalStateException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>IllegalStateException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static IllegalStateException createIllegalStateException(final Localizable pattern,
+ final Object ... arguments) {
+ return new IllegalStateException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6880901520234515725L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>ConcurrentModificationException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createConcurrentModificationException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static ConcurrentModificationException createConcurrentModificationException(final String pattern,
+ final Object ... arguments) {
+ return createConcurrentModificationException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>ConcurrentModificationException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static ConcurrentModificationException createConcurrentModificationException(final Localizable pattern,
+ final Object ... arguments) {
+ return new ConcurrentModificationException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1878427236170442052L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>NoSuchElementException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createNoSuchElementException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static NoSuchElementException createNoSuchElementException(final String pattern,
+ final Object ... arguments) {
+ return createNoSuchElementException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>NoSuchElementException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static NoSuchElementException createNoSuchElementException(final Localizable pattern,
+ final Object ... arguments) {
+ return new NoSuchElementException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1632410088350355086L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>UnsupportedOperationException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ * @deprecated in 2.2. Please use {@link org.apache.commons.math.exception.MathUnsupportedOperationException}
+ * instead.
+ */
+ @Deprecated
+ public static UnsupportedOperationException createUnsupportedOperationException(final Localizable pattern,
+ final Object ... arguments) {
+ return new UnsupportedOperationException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4284649691002411505L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>NullPointerException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createNullPointerException(Localizable, Object...)}
+ */
+ @Deprecated
+ public static NullPointerException createNullPointerException(final String pattern,
+ final Object ... arguments) {
+ return createNullPointerException(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>NullPointerException</code> with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ * @deprecated in 2.2. Checks for "null" must not be performed in Commons-Math.
+ */
+ @Deprecated
+ public static NullPointerException createNullPointerException(final Localizable pattern,
+ final Object ... arguments) {
+ return new NullPointerException() {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 451965530686593945L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /**
+ * Constructs a new <code>ParseException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param offset offset at which error occurred
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @deprecated as of 2.2 replaced by {@link #createParseException(int, Localizable, Object...)}
+ */
+ @Deprecated
+ public static ParseException createParseException(final int offset,
+ final String pattern,
+ final Object ... arguments) {
+ return createParseException(offset, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new <code>ParseException</code> with specified
+ * formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param offset offset at which error occurred
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @return built exception
+ * @since 2.2
+ */
+ public static ParseException createParseException(final int offset,
+ final Localizable pattern,
+ final Object ... arguments) {
+ return new ParseException(null, offset) {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8153587599409010120L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, pattern, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), pattern, arguments);
+ }
+
+ };
+ }
+
+ /** Create an {@link java.lang.RuntimeException} for an internal error.
+ * @param cause underlying cause
+ * @return an {@link java.lang.RuntimeException} for an internal error
+ */
+ public static RuntimeException createInternalError(final Throwable cause) {
+
+ final String argument = "https://issues.apache.org/jira/browse/MATH";
+
+ return new RuntimeException(cause) {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -201865440834027016L;
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return buildMessage(Locale.US, LocalizedFormats.INTERNAL_ERROR, argument);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return buildMessage(Locale.getDefault(), LocalizedFormats.INTERNAL_ERROR, argument);
+ }
+
+ };
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/MaxEvaluationsExceededException.java b/src/main/java/org/apache/commons/math/MaxEvaluationsExceededException.java
new file mode 100644
index 0000000..06730bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/MaxEvaluationsExceededException.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.math;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a numerical computation exceeds its allowed
+ * number of functions evaluations.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class MaxEvaluationsExceededException extends ConvergenceException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5921271447220129118L;
+
+ /** Maximal number of evaluations allowed. */
+ private final int maxEvaluations;
+
+ /**
+ * Constructs an exception with a default detail message.
+ * @param maxEvaluations maximal number of evaluations allowed
+ */
+ public MaxEvaluationsExceededException(final int maxEvaluations) {
+ super(LocalizedFormats.MAX_EVALUATIONS_EXCEEDED, maxEvaluations);
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param maxEvaluations the exceeded maximal number of evaluations
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MaxEvaluationsExceededException(int, Localizable, Object...)}
+ */
+ @Deprecated
+ public MaxEvaluationsExceededException(final int maxEvaluations,
+ final String pattern, final Object ... arguments) {
+ this(maxEvaluations, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param maxEvaluations the exceeded maximal number of evaluations
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MaxEvaluationsExceededException(final int maxEvaluations,
+ final Localizable pattern, final Object ... arguments) {
+ super(pattern, arguments);
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** Get the maximal number of evaluations allowed.
+ * @return maximal number of evaluations allowed
+ */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/MaxIterationsExceededException.java b/src/main/java/org/apache/commons/math/MaxIterationsExceededException.java
new file mode 100644
index 0000000..51b2cce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/MaxIterationsExceededException.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.math;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a numerical computation exceeds its allowed
+ * number of iterations.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class MaxIterationsExceededException extends ConvergenceException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7821226672760574694L;
+
+ /** Maximal number of iterations allowed. */
+ private final int maxIterations;
+
+ /**
+ * Constructs an exception with a default detail message.
+ * @param maxIterations maximal number of iterations allowed
+ */
+ public MaxIterationsExceededException(final int maxIterations) {
+ super(LocalizedFormats.MAX_ITERATIONS_EXCEEDED, maxIterations);
+ this.maxIterations = maxIterations;
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param maxIterations the exceeded maximal number of iterations
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MaxIterationsExceededException(int, Localizable, Object...)}
+ */
+ @Deprecated
+ public MaxIterationsExceededException(final int maxIterations,
+ final String pattern, final Object ... arguments) {
+ this(maxIterations, new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param maxIterations the exceeded maximal number of iterations
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MaxIterationsExceededException(final int maxIterations,
+ final Localizable pattern, final Object ... arguments) {
+ super(pattern, arguments);
+ this.maxIterations = maxIterations;
+ }
+
+ /** Get the maximal number of iterations allowed.
+ * @return maximal number of iterations allowed
+ */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/BinaryFunction.java b/src/main/java/org/apache/commons/math/analysis/BinaryFunction.java
new file mode 100644
index 0000000..227b72e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/BinaryFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.util.FastMath;
+
+
+
+/**
+ * Base class for {@link BivariateRealFunction} that can be composed with other functions.
+ *
+ * @since 2.1
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ * @deprecated in 2.2
+ */
+@Deprecated
+public abstract class BinaryFunction implements BivariateRealFunction {
+
+ /** The + operator method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction ADD = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return x + y;
+ }
+ };
+
+ /** The - operator method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction SUBTRACT = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return x - y;
+ }
+ };
+
+ /** The * operator method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction MULTIPLY = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return x * y;
+ }
+ };
+
+ /** The / operator method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction DIVIDE = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return x / y;
+ }
+ };
+
+ /** The {@code FastMath.pow} method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction POW = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return FastMath.pow(x, y);
+ }
+ };
+
+ /** The {@code FastMath.atan2} method wrapped as a {@link BinaryFunction}. */
+ public static final BinaryFunction ATAN2 = new BinaryFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double y) {
+ return FastMath.atan2(x, y);
+ }
+ };
+
+ /** {@inheritDoc} */
+ public abstract double value(double x, double y) throws FunctionEvaluationException;
+
+ /** Get a composable function by fixing the first argument of the instance.
+ * @param fixedX fixed value of the first argument
+ * @return a function such that {@code f.value(y) == value(fixedX, y)}
+ */
+ public ComposableFunction fix1stArgument(final double fixedX) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return BinaryFunction.this.value(fixedX, x);
+ }
+ };
+ }
+
+ /** Get a composable function by fixing the second argument of the instance.
+ * @param fixedY fixed value of the second argument
+ * @return a function such that {@code f.value(x) == value(x, fixedY)}
+ */
+ public ComposableFunction fix2ndArgument(final double fixedY) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return BinaryFunction.this.value(x, fixedY);
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.java
new file mode 100644
index 0000000..4b12312
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/BivariateRealFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a bivariate real function.
+ *
+ * @since 2.1
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ */
+public interface BivariateRealFunction {
+ /**
+ * 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.
+ * @throws FunctionEvaluationException if the function evaluation fails.
+ */
+ double value(double x, double y)
+ throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java b/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java
new file mode 100644
index 0000000..91c8132
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/ComposableFunction.java
@@ -0,0 +1,506 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * Base class for {@link UnivariateRealFunction} that can be composed with other functions.
+ *
+ * @since 2.1
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public abstract class ComposableFunction implements UnivariateRealFunction {
+
+ /** The constant function always returning 0. */
+ public static final ComposableFunction ZERO = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return 0;
+ }
+ };
+
+ /** The constant function always returning 1. */
+ public static final ComposableFunction ONE = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return 1;
+ }
+ };
+
+ /** The identity function. */
+ public static final ComposableFunction IDENTITY = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return d;
+ }
+ };
+
+ /** The {@code FastMath.abs} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction ABS = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.abs(d);
+ }
+ };
+
+ /** The - operator wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction NEGATE = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return -d;
+ }
+ };
+
+ /** The invert operator wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction INVERT = new ComposableFunction () {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d){
+ return 1/d;
+ }
+ };
+
+ /** The {@code FastMath.sin} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction SIN = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.sin(d);
+ }
+ };
+
+ /** The {@code FastMath.sqrt} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction SQRT = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.sqrt(d);
+ }
+ };
+
+ /** The {@code FastMath.sinh} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction SINH = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.sinh(d);
+ }
+ };
+
+ /** The {@code FastMath.exp} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction EXP = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.exp(d);
+ }
+ };
+
+ /** The {@code FastMath.expm1} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction EXPM1 = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.expm1(d);
+ }
+ };
+
+ /** The {@code FastMath.asin} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction ASIN = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.asin(d);
+ }
+ };
+
+ /** The {@code FastMath.atan} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction ATAN = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.atan(d);
+ }
+ };
+
+ /** The {@code FastMath.tan} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction TAN = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.tan(d);
+ }
+ };
+
+ /** The {@code FastMath.tanh} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction TANH = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.tanh(d);
+ }
+ };
+
+ /** The {@code FastMath.cbrt} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction CBRT = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.cbrt(d);
+ }
+ };
+
+ /** The {@code FastMath.ceil} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction CEIL = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.ceil(d);
+ }
+ };
+
+ /** The {@code FastMath.floor} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction FLOOR = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.floor(d);
+ }
+ };
+
+ /** The {@code FastMath.log} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction LOG = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.log(d);
+ }
+ };
+
+ /** The {@code FastMath.log10} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction LOG10 = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.log10(d);
+ }
+ };
+
+ /** The {@code FastMath.log1p} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction LOG1P = new ComposableFunction () {
+ @Override
+ public double value(double d){
+ return FastMath.log1p(d);
+ }
+ };
+
+ /** The {@code FastMath.cos} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction COS = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.cos(d);
+ }
+ };
+
+ /** The {@code FastMath.abs} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction ACOS = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.acos(d);
+ }
+ };
+
+ /** The {@code FastMath.cosh} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction COSH = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.cosh(d);
+ }
+ };
+
+ /** The {@code FastMath.rint} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction RINT = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.rint(d);
+ }
+ };
+
+ /** The {@code FastMath.signum} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction SIGNUM = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.signum(d);
+ }
+ };
+
+ /** The {@code FastMath.ulp} method wrapped as a {@link ComposableFunction}. */
+ public static final ComposableFunction ULP = new ComposableFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double d) {
+ return FastMath.ulp(d);
+ }
+ };
+
+ /** Precompose the instance with another function.
+ * <p>
+ * The composed function h created by {@code h = g.of(f)} is such
+ * that {@code h.value(x) == g.value(f.value(x))} for all x.
+ * </p>
+ * @param f function to compose with
+ * @return a new function which computes {@code this.value(f.value(x))}
+ * @see #postCompose(UnivariateRealFunction)
+ */
+ public ComposableFunction of(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(f.value(x));
+ }
+ };
+ }
+
+ /** Postcompose the instance with another function.
+ * <p>
+ * The composed function h created by {@code h = g.postCompose(f)} is such
+ * that {@code h.value(x) == f.value(g.value(x))} for all x.
+ * </p>
+ * @param f function to compose with
+ * @return a new function which computes {@code f.value(this.value(x))}
+ * @see #of(UnivariateRealFunction)
+ */
+ public ComposableFunction postCompose(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return f.value(ComposableFunction.this.value(x));
+ }
+ };
+ }
+
+ /**
+ * Return a function combining the instance and another function.
+ * <p>
+ * The function h created by {@code h = g.combine(f, combiner)} is such that
+ * {@code h.value(x) == combiner.value(g.value(x), f.value(x))} for all x.
+ * </p>
+ * @param f function to combine with the instance
+ * @param combiner bivariate function used for combining
+ * @return a new function which computes {@code combine.value(this.value(x), f.value(x))}
+ */
+ public ComposableFunction combine(final UnivariateRealFunction f,
+ final BivariateRealFunction combiner) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return combiner.value(ComposableFunction.this.value(x), f.value(x));
+ }
+ };
+ }
+
+ /**
+ * Return a function adding the instance and another function.
+ * @param f function to combine with the instance
+ * @return a new function which computes {@code this.value(x) + f.value(x)}
+ */
+ public ComposableFunction add(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) + f.value(x);
+ }
+ };
+ }
+
+ /**
+ * Return a function adding a constant term to the instance.
+ * @param a term to add
+ * @return a new function which computes {@code this.value(x) + a}
+ */
+ public ComposableFunction add(final double a) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) + a;
+ }
+ };
+ }
+
+ /**
+ * Return a function subtracting another function from the instance.
+ * @param f function to combine with the instance
+ * @return a new function which computes {@code this.value(x) - f.value(x)}
+ */
+ public ComposableFunction subtract(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) - f.value(x);
+ }
+ };
+ }
+
+ /**
+ * Return a function multiplying the instance and another function.
+ * @param f function to combine with the instance
+ * @return a new function which computes {@code this.value(x) * f.value(x)}
+ */
+ public ComposableFunction multiply(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) * f.value(x);
+ }
+ };
+ }
+
+ /**
+ * Return a function scaling the instance by a constant factor.
+ * @param scaleFactor constant scaling factor
+ * @return a new function which computes {@code this.value(x) * scaleFactor}
+ */
+ public ComposableFunction multiply(final double scaleFactor) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) * scaleFactor;
+ }
+ };
+ }
+ /**
+ * Return a function dividing the instance by another function.
+ * @param f function to combine with the instance
+ * @return a new function which computes {@code this.value(x) / f.value(x)}
+ */
+ public ComposableFunction divide(final UnivariateRealFunction f) {
+ return new ComposableFunction() {
+ @Override
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+ return ComposableFunction.this.value(x) / f.value(x);
+ }
+ };
+ }
+
+ /**
+ * Generates a function that iteratively apply instance function on all
+ * elements of an array.
+ * <p>
+ * The generated function behaves as follows:
+ * <ul>
+ * <li>initialize result = initialValue</li>
+ * <li>iterate: {@code result = combiner.value(result,
+ * this.value(nextMultivariateEntry));}</li>
+ * <li>return result</li>
+ * </ul>
+ * </p>
+ * @param combiner combiner to use between entries
+ * @param initialValue initial value to use before first entry
+ * @return a new function that iteratively apply instance function on all
+ * elements of an array.
+ */
+ public MultivariateRealFunction asCollector(final BivariateRealFunction combiner,
+ final double initialValue) {
+ return new MultivariateRealFunction() {
+ /** {@inheritDoc} */
+ public double value(double[] point)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ double result = initialValue;
+ for (final double entry : point) {
+ result = combiner.value(result, ComposableFunction.this.value(entry));
+ }
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Generates a function that iteratively apply instance function on all
+ * elements of an array.
+ * <p>
+ * Calling this method is equivalent to call {@link
+ * #asCollector(BivariateRealFunction, double) asCollector(BivariateRealFunction, 0.0)}.
+ * </p>
+ * @param combiner combiner to use between entries
+ * @return a new function that iteratively apply instance function on all
+ * elements of an array.
+ * @see #asCollector(BivariateRealFunction, double)
+ */
+ public MultivariateRealFunction asCollector(final BivariateRealFunction combiner) {
+ return asCollector(combiner, 0.0);
+ }
+
+ /**
+ * Generates a function that iteratively apply instance function on all
+ * elements of an array.
+ * <p>
+ * Calling this method is equivalent to call {@link
+ * #asCollector(BivariateRealFunction, double) asCollector(BinaryFunction.ADD, initialValue)}.
+ * </p>
+ * @param initialValue initial value to use before first entry
+ * @return a new function that iteratively apply instance function on all
+ * elements of an array.
+ * @see #asCollector(BivariateRealFunction, double)
+ * @see BinaryFunction#ADD
+ */
+ public MultivariateRealFunction asCollector(final double initialValue) {
+ return asCollector(BinaryFunction.ADD, initialValue);
+ }
+
+ /**
+ * Generates a function that iteratively apply instance function on all
+ * elements of an array.
+ * <p>
+ * Calling this method is equivalent to call {@link
+ * #asCollector(BivariateRealFunction, double) asCollector(BinaryFunction.ADD, 0.0)}.
+ * </p>
+ * @return a new function that iteratively apply instance function on all
+ * elements of an array.
+ * @see #asCollector(BivariateRealFunction, double)
+ * @see BinaryFunction#ADD
+ */
+ public MultivariateRealFunction asCollector() {
+ return asCollector(BinaryFunction.ADD, 0.0);
+ }
+
+ /** {@inheritDoc} */
+ public abstract double value(double x) throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.java
new file mode 100644
index 0000000..8d4f0c9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.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.math.analysis;
+
+/**
+ * Extension of {@link MultivariateRealFunction} representing a differentiable
+ * multivariate real function.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public interface DifferentiableMultivariateRealFunction extends MultivariateRealFunction {
+
+ /**
+ * 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.
+ * </p>
+ * @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
+ */
+ MultivariateRealFunction 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.</p>
+ * @return the gradient function
+ */
+ MultivariateVectorialFunction gradient();
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.java
new file mode 100644
index 0000000..cc4ab8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.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.math.analysis;
+
+
+/**
+ * Extension of {@link MultivariateVectorialFunction} representing a differentiable
+ * multivariate vectorial function.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public interface DifferentiableMultivariateVectorialFunction
+ extends MultivariateVectorialFunction {
+
+ /**
+ * Returns the jacobian function.
+ * @return the jacobian function
+ */
+ MultivariateMatrixFunction jacobian();
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.java
new file mode 100644
index 0000000..5f2f11b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateMatrixFunction.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.math.analysis;
+
+/**
+ * Extension of {@link UnivariateMatrixFunction} representing a differentiable univariate matrix function.
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @since 2.0
+ */
+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/math/analysis/DifferentiableUnivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.java
new file mode 100644
index 0000000..1faaad3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateRealFunction.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.math.analysis;
+
+/**
+ * Extension of {@link UnivariateRealFunction} representing a differentiable univariate real function.
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+public interface DifferentiableUnivariateRealFunction
+ extends UnivariateRealFunction {
+
+ /**
+ * Returns the derivative of the function
+ *
+ * @return the derivative function
+ */
+ UnivariateRealFunction derivative();
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.java
new file mode 100644
index 0000000..6566afe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/DifferentiableUnivariateVectorialFunction.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.math.analysis;
+
+/**
+ * Extension of {@link UnivariateVectorialFunction} representing a differentiable univariate vectorial function.
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @since 2.0
+ */
+public interface DifferentiableUnivariateVectorialFunction
+ extends UnivariateVectorialFunction {
+
+ /**
+ * Returns the derivative of the function
+ *
+ * @return the derivative function
+ */
+ UnivariateVectorialFunction derivative();
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.java
new file mode 100644
index 0000000..a5bad26
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/MultivariateMatrixFunction.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.math.analysis;
+
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a multivariate matrix function.
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 FunctionEvaluationException if the function evaluation fails
+ * @exception IllegalArgumentException if points dimension is wrong
+ */
+ double[][] value(double[] point)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.java
new file mode 100644
index 0000000..f054c51
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/MultivariateRealFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a multivariate real function.
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public interface MultivariateRealFunction {
+
+ /**
+ * 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 FunctionEvaluationException if the function evaluation fails
+ * @exception IllegalArgumentException if points dimension is wrong
+ */
+ double value(double[] point)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.java
new file mode 100644
index 0000000..7e83a7c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/MultivariateVectorialFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a multivariate vectorial function.
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public interface MultivariateVectorialFunction {
+
+ /**
+ * 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 FunctionEvaluationException if the function evaluation fails
+ * @exception IllegalArgumentException if points dimension is wrong
+ */
+ double[] value(double[] point)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.java
new file mode 100644
index 0000000..3ebf24d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/TrivariateRealFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a trivariate real function.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public interface TrivariateRealFunction {
+ /**
+ * 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.
+ * @throws FunctionEvaluationException if the function evaluation fails.
+ */
+ double value(double x, double y, double z)
+ throws FunctionEvaluationException;
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.java
new file mode 100644
index 0000000..2db7349
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/UnivariateMatrixFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a univariate matrix function.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ * @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
+ * @throws FunctionEvaluationException if the function evaluation fails
+ */
+ double[][] value(double x) throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.java
new file mode 100644
index 0000000..298d8a7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/UnivariateRealFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a univariate real function.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public interface UnivariateRealFunction {
+
+ /**
+ * Compute the value for the function.
+ * @param x the point for which the function value should be computed
+ * @return the value
+ * @throws FunctionEvaluationException if the function evaluation fails
+ */
+ double value(double x) throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java b/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.java
new file mode 100644
index 0000000..64e7e15
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/UnivariateVectorialFunction.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.math.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a univariate vectorial function.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+ */
+public interface UnivariateVectorialFunction {
+
+ /**
+ * Compute the value for the function.
+ * @param x the point for which the function value should be computed
+ * @return the value
+ * @throws FunctionEvaluationException if the function evaluation fails
+ */
+ double[] value(double x) throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.java
new file mode 100644
index 0000000..db6b76c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.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.math.analysis.integration;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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 functions evaluations. A
+ * Legendre-Gauss integrator using an n-points quadrature formula can
+ * integrate exactly 2n-1 degree polynomials.
+ * </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>
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+
+public class LegendreGaussIntegrator extends UnivariateRealIntegratorImpl {
+
+ /** 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.
+ * @param n number of points desired (must be between 2 and 5 inclusive)
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @exception IllegalArgumentException if the number of points is not
+ * in the supported range
+ */
+ public LegendreGaussIntegrator(final int n, final int defaultMaximalIterationCount)
+ throws IllegalArgumentException {
+ super(defaultMaximalIterationCount);
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED,
+ n, 2, 5);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double integrate(final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException {
+ return integrate(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(final UnivariateRealFunction f, final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException {
+
+ clearResult();
+ verifyInterval(min, max);
+ verifyIterationCount();
+
+ // compute first estimate with a single step
+ double oldt = stage(f, min, max, 1);
+
+ int n = 2;
+ for (int i = 0; i < maximalIterationCount; ++i) {
+
+ // improve integral with a larger number of steps
+ final double t = stage(f, min, max, n);
+
+ // estimate error
+ final double delta = FastMath.abs(t - oldt);
+ final double limit =
+ FastMath.max(absoluteAccuracy,
+ relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5);
+
+ // check convergence
+ if ((i + 1 >= minimalIterationCount) && (delta <= limit)) {
+ setResult(t, i);
+ return result;
+ }
+
+ // 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;
+
+ }
+
+ throw new MaxIterationsExceededException(maximalIterationCount);
+
+ }
+
+ /**
+ * Compute the n-th stage integral.
+ * @param f the integrand function
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n number of steps
+ * @return the value of n-th stage integral
+ * @throws FunctionEvaluationException if an error occurs evaluating the
+ * function
+ */
+ private double stage(final UnivariateRealFunction f,
+ final double min, final double max, final int n)
+ throws FunctionEvaluationException {
+
+ // set up the step for the current stage
+ final double step = (max - min) / n;
+ final double halfStep = step / 2.0;
+
+ // integrate over all elementary steps
+ double midPoint = min + halfStep;
+ double sum = 0.0;
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < abscissas.length; ++j) {
+ sum += weights[j] * f.value(midPoint + halfStep * abscissas[j]);
+ }
+ midPoint += step;
+ }
+
+ return halfStep * sum;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.java
new file mode 100644
index 0000000..9650af5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/RombergIntegrator.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.math.analysis.integration;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class RombergIntegrator extends UnivariateRealIntegratorImpl {
+
+ /**
+ * Construct an integrator for the given function.
+ *
+ * @param f function to integrate
+ * @deprecated as of 2.0 the integrand function is passed as an argument
+ * to the {@link #integrate(UnivariateRealFunction, double, double)}method.
+ */
+ @Deprecated
+ public RombergIntegrator(UnivariateRealFunction f) {
+ super(f, 32);
+ }
+
+ /**
+ * Construct an integrator.
+ */
+ public RombergIntegrator() {
+ super(32);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double integrate(final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+ return integrate(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(final UnivariateRealFunction f, final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+
+ final int m = maximalIterationCount + 1;
+ double previousRow[] = new double[m];
+ double currentRow[] = new double[m];
+
+ clearResult();
+ verifyInterval(min, max);
+ verifyIterationCount();
+
+ TrapezoidIntegrator qtrap = new TrapezoidIntegrator();
+ currentRow[0] = qtrap.stage(f, min, max, 0);
+ double olds = currentRow[0];
+ for (int i = 1; i <= maximalIterationCount; ++i) {
+
+ // switch rows
+ final double[] tmpRow = previousRow;
+ previousRow = currentRow;
+ currentRow = tmpRow;
+
+ currentRow[0] = qtrap.stage(f, min, max, i);
+ 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 >= minimalIterationCount) {
+ final double delta = FastMath.abs(s - olds);
+ final double rLimit = relativeAccuracy * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5;
+ if ((delta <= rLimit) || (delta <= absoluteAccuracy)) {
+ setResult(s, i);
+ return result;
+ }
+ }
+ olds = s;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void verifyIterationCount() throws IllegalArgumentException {
+ super.verifyIterationCount();
+ // at most 32 bisection refinements due to higher order divider
+ if (maximalIterationCount > 32) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_ITERATIONS_LIMITS,
+ 0, 32);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.java
new file mode 100644
index 0000000..045b54e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/SimpsonIntegrator.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.math.analysis.integration;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <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 basic trapezoid rule as building blocks to
+ * calculate the Simpson's rule of alternating 2/3 and 4/3.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class SimpsonIntegrator extends UnivariateRealIntegratorImpl {
+
+ /**
+ * Construct an integrator for the given function.
+ *
+ * @param f function to integrate
+ * @deprecated as of 2.0 the integrand function is passed as an argument
+ * to the {@link #integrate(UnivariateRealFunction, double, double)}method.
+ */
+ @Deprecated
+ public SimpsonIntegrator(UnivariateRealFunction f) {
+ super(f, 64);
+ }
+
+ /**
+ * Construct an integrator.
+ */
+ public SimpsonIntegrator() {
+ super(64);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double integrate(final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+ return integrate(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(final UnivariateRealFunction f, final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+
+ clearResult();
+ verifyInterval(min, max);
+ verifyIterationCount();
+
+ TrapezoidIntegrator qtrap = new TrapezoidIntegrator();
+ if (minimalIterationCount == 1) {
+ final double s = (4 * qtrap.stage(f, min, max, 1) - qtrap.stage(f, min, max, 0)) / 3.0;
+ setResult(s, 1);
+ return result;
+ }
+ // Simpson's rule requires at least two trapezoid stages.
+ double olds = 0;
+ double oldt = qtrap.stage(f, min, max, 0);
+ for (int i = 1; i <= maximalIterationCount; ++i) {
+ final double t = qtrap.stage(f, min, max, i);
+ final double s = (4 * t - oldt) / 3.0;
+ if (i >= minimalIterationCount) {
+ final double delta = FastMath.abs(s - olds);
+ final double rLimit =
+ relativeAccuracy * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5;
+ if ((delta <= rLimit) || (delta <= absoluteAccuracy)) {
+ setResult(s, i);
+ return result;
+ }
+ }
+ olds = s;
+ oldt = t;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void verifyIterationCount() throws IllegalArgumentException {
+ super.verifyIterationCount();
+ // at most 64 bisection refinements
+ if (maximalIterationCount > 64) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_ITERATIONS_LIMITS,
+ 0, 64);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.java
new file mode 100644
index 0000000..88903f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.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.math.analysis.integration;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/TrapezoidalRule.html">
+ * Trapezoidal 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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class TrapezoidIntegrator extends UnivariateRealIntegratorImpl {
+
+ /** Intermediate result. */
+ private double s;
+
+ /**
+ * Construct an integrator for the given function.
+ *
+ * @param f function to integrate
+ * @deprecated as of 2.0 the integrand function is passed as an argument
+ * to the {@link #integrate(UnivariateRealFunction, double, double)}method.
+ */
+ @Deprecated
+ public TrapezoidIntegrator(UnivariateRealFunction f) {
+ super(f, 64);
+ }
+
+ /**
+ * Construct an integrator.
+ */
+ public TrapezoidIntegrator() {
+ super(64);
+ }
+
+ /**
+ * 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
+ * alrealy computed values.</p>
+ *
+ * @param f the integrand function
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the stage of 1/2 refinement, n = 0 is no refinement
+ * @return the value of n-th stage integral
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ */
+ double stage(final UnivariateRealFunction f,
+ final double min, final double max, final int n)
+ throws FunctionEvaluationException {
+
+ if (n == 0) {
+ s = 0.5 * (max - min) * (f.value(min) + f.value(max));
+ return s;
+ } else {
+ final long np = 1L << (n-1); // number of new points in this stage
+ double sum = 0;
+ final double spacing = (max - min) / np; // spacing between adjacent new points
+ double x = min + 0.5 * spacing; // the first new point
+ for (long i = 0; i < np; i++) {
+ sum += f.value(x);
+ x += spacing;
+ }
+ // add the new sum to previously calculated result
+ s = 0.5 * (s + sum * spacing);
+ return s;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double integrate(final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+ return integrate(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(final UnivariateRealFunction f, final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException, IllegalArgumentException {
+
+ clearResult();
+ verifyInterval(min, max);
+ verifyIterationCount();
+
+ double oldt = stage(f, min, max, 0);
+ for (int i = 1; i <= maximalIterationCount; ++i) {
+ final double t = stage(f, min, max, i);
+ if (i >= minimalIterationCount) {
+ final double delta = FastMath.abs(t - oldt);
+ final double rLimit =
+ relativeAccuracy * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5;
+ if ((delta <= rLimit) || (delta <= absoluteAccuracy)) {
+ setResult(t, i);
+ return result;
+ }
+ }
+ oldt = t;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void verifyIterationCount() throws IllegalArgumentException {
+ super.verifyIterationCount();
+ // at most 64 bisection refinements
+ if (maximalIterationCount > 64) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_ITERATIONS_LIMITS,
+ 0, 64);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.java
new file mode 100644
index 0000000..184bb44
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.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.math.analysis.integration;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.ConvergingAlgorithm;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+/**
+ * Interface for univariate real integration algorithms.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public interface UnivariateRealIntegrator extends ConvergingAlgorithm {
+
+ /**
+ * Set the lower limit for the number of iterations.
+ * <p>
+ * 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.</p>
+ * <p>
+ * A <code>ConvergenceException</code> will be thrown if this number
+ * is not met.</p>
+ *
+ * @param count minimum number of iterations
+ */
+ void setMinimalIterationCount(int count);
+
+ /**
+ * Get the lower limit for the number of iterations.
+ *
+ * @return the actual lower limit
+ */
+ int getMinimalIterationCount();
+
+ /**
+ * Reset the lower limit for the number of iterations to the default.
+ * <p>
+ * The default value is supplied by the implementation.</p>
+ *
+ * @see #setMinimalIterationCount(int)
+ */
+ void resetMinimalIterationCount();
+
+ /**
+ * Integrate the function in the given interval.
+ *
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the value of integral
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the integrator detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the
+ * function
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the integrator
+ * @deprecated replaced by {@link #integrate(UnivariateRealFunction, double, double)}
+ * since 2.0
+ */
+ @Deprecated
+ double integrate(double min, double max)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException;
+
+ /**
+ * Integrate the function in the given interval.
+ *
+ * @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 ConvergenceException if the maximum iteration count is exceeded
+ * or the integrator detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the integrator
+ */
+ double integrate(UnivariateRealFunction f, double min, double max)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException;
+
+ /**
+ * Get the result of the last run of the integrator.
+ *
+ * @return the last result
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed
+ */
+ double getResult() throws IllegalStateException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.java
new file mode 100644
index 0000000..655a852
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/UnivariateRealIntegratorImpl.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.math.analysis.integration;
+
+import org.apache.commons.math.ConvergingAlgorithmImpl;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * Provide a default implementation for several generic functions.
+ *
+ * @version $Revision: 1072409 $ $Date: 2011-02-19 19:50:36 +0100 (sam. 19 févr. 2011) $
+ * @since 1.2
+ */
+public abstract class UnivariateRealIntegratorImpl
+ extends ConvergingAlgorithmImpl implements UnivariateRealIntegrator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6248808456637441533L;
+
+ /** minimum number of iterations */
+ protected int minimalIterationCount;
+
+ /** default minimum number of iterations */
+ protected int defaultMinimalIterationCount;
+
+ /** indicates whether an integral has been computed */
+ protected boolean resultComputed = false;
+
+ /** the last computed integral */
+ protected double result;
+
+ /**
+ * The integrand function.
+ *
+ * @deprecated as of 2.0 the integrand function is passed as an argument
+ * to the {@link #integrate(UnivariateRealFunction, double, double)}method.
+ */
+ @Deprecated
+ protected UnivariateRealFunction f;
+
+ /**
+ * Construct an integrator with given iteration count and accuracy.
+ *
+ * @param f the integrand function
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the iteration
+ * limits are not valid
+ * @deprecated as of 2.0 the integrand function is passed as an argument
+ * to the {@link #integrate(UnivariateRealFunction, double, double)}method.
+ */
+ @Deprecated
+ protected UnivariateRealIntegratorImpl(final UnivariateRealFunction f,
+ final int defaultMaximalIterationCount)
+ throws IllegalArgumentException {
+ super(defaultMaximalIterationCount, 1.0e-15);
+ if (f == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+
+ this.f = f;
+
+ // parameters that are problem specific
+ setRelativeAccuracy(1.0e-6);
+ this.defaultMinimalIterationCount = 3;
+ this.minimalIterationCount = defaultMinimalIterationCount;
+
+ verifyIterationCount();
+ }
+
+ /**
+ * Construct an integrator with given iteration count and accuracy.
+ *
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the iteration
+ * limits are not valid
+ */
+ protected UnivariateRealIntegratorImpl(final int defaultMaximalIterationCount)
+ throws IllegalArgumentException {
+ super(defaultMaximalIterationCount, 1.0e-15);
+
+ // parameters that are problem specific
+ setRelativeAccuracy(1.0e-6);
+ this.defaultMinimalIterationCount = 3;
+ this.minimalIterationCount = defaultMinimalIterationCount;
+
+ verifyIterationCount();
+ }
+
+ /**
+ * Access the last computed integral.
+ *
+ * @return the last computed integral
+ * @throws IllegalStateException if no integral has been computed
+ */
+ public double getResult() throws IllegalStateException {
+ if (resultComputed) {
+ return result;
+ } else {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_RESULT_AVAILABLE);
+ }
+ }
+
+ /**
+ * Convenience function for implementations.
+ *
+ * @param newResult the result to set
+ * @param iterationCount the iteration count to set
+ */
+ protected final void setResult(double newResult, int iterationCount) {
+ this.result = newResult;
+ this.iterationCount = iterationCount;
+ this.resultComputed = true;
+ }
+
+ /**
+ * Convenience function for implementations.
+ */
+ protected final void clearResult() {
+ this.iterationCount = 0;
+ this.resultComputed = false;
+ }
+
+ /** {@inheritDoc} */
+ public void setMinimalIterationCount(int count) {
+ minimalIterationCount = count;
+ }
+
+ /** {@inheritDoc} */
+ public int getMinimalIterationCount() {
+ return minimalIterationCount;
+ }
+
+ /** {@inheritDoc} */
+ public void resetMinimalIterationCount() {
+ minimalIterationCount = defaultMinimalIterationCount;
+ }
+
+ /**
+ * Verifies that the endpoints specify an interval.
+ *
+ * @param lower lower endpoint
+ * @param upper upper endpoint
+ * @throws IllegalArgumentException if not interval
+ */
+ protected void verifyInterval(double lower, double upper) throws
+ IllegalArgumentException {
+ if (lower >= upper) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper);
+ }
+ }
+
+ /**
+ * Verifies that the upper and lower limits of iterations are valid.
+ *
+ * @throws IllegalArgumentException if not valid
+ */
+ protected void verifyIterationCount() throws IllegalArgumentException {
+ if ((minimalIterationCount <= 0) || (maximalIterationCount <= minimalIterationCount)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_ITERATIONS_LIMITS,
+ minimalIterationCount, maximalIterationCount);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/integration/package.html b/src/main/java/org/apache/commons/math/analysis/integration/package.html
new file mode 100644
index 0000000..5bbab0e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/integration/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Numerical integration (quadrature) algorithms for univariate real functions.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..c786a4d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.java
@@ -0,0 +1,558 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
+ * bicubic spline interpolation</a>.
+ *
+ * @version $Revision$ $Date$
+ * @since 2.1
+ */
+public class BicubicSplineInterpolatingFunction
+ implements BivariateRealFunction {
+ /**
+ * 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 BivariateRealFunction[][][] partialDerivatives = null;
+
+ /**
+ * @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 org.apache.commons.math.exception.NonMonotonousSequenceException
+ * 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 {
+ 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);
+ }
+
+ MathUtils.checkOrder(x);
+ MathUtils.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));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x, double y) {
+ 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 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);
+ }
+
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the first partial derivative with
+ * respect to x.
+ * @since 2.2
+ */
+ public double partialDerivativeX(double x, double y) {
+ 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.
+ * @since 2.2
+ */
+ public double partialDerivativeY(double x, double y) {
+ 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.
+ * @since 2.2
+ */
+ public double partialDerivativeXX(double x, double y) {
+ 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.
+ * @since 2.2
+ */
+ public double partialDerivativeYY(double x, double y) {
+ 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.
+ * @since 2.2
+ */
+ public double partialDerivativeXY(double x, double y) {
+ 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 FunctionEvaluationException
+ */
+ private double partialDerivative(int which, double x, double y) {
+ if (partialDerivatives == null) {
+ computePartialDerivatives();
+ }
+
+ 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 double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+ try {
+ return partialDerivatives[which][i][j].value(xN, yN);
+ } catch (FunctionEvaluationException fee) {
+ // this should never happen
+ throw new RuntimeException(fee);
+ }
+
+ }
+
+ /**
+ * Compute all partial derivatives.
+ */
+ private void computePartialDerivatives() {
+ final int lastI = xval.length - 1;
+ final int lastJ = yval.length - 1;
+ partialDerivatives = new BivariateRealFunction[5][lastI][lastJ];
+
+ for (int i = 0; i < lastI; i++) {
+ for (int j = 0; j < lastJ; j++) {
+ final BicubicSplineFunction f = splines[i][j];
+ partialDerivatives[0][i][j] = f.partialDerivativeX();
+ partialDerivatives[1][i][j] = f.partialDerivativeY();
+ partialDerivatives[2][i][j] = f.partialDerivativeXX();
+ partialDerivatives[3][i][j] = f.partialDerivativeYY();
+ partialDerivatives[4][i][j] = f.partialDerivativeXY();
+ }
+ }
+ }
+
+ /**
+ * @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)</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[16];
+
+ for (int i = 0; i < 16; i++) {
+ double result = 0;
+ final double[] row = AINV[i];
+ for (int j = 0; j < 16; j++) {
+ result += row[j] * beta[j];
+ }
+ a[i] = result;
+ }
+
+ return a;
+ }
+}
+
+/**
+ * 2D-spline function.
+ *
+ * @version $Revision$ $Date$
+ */
+class BicubicSplineFunction
+ implements BivariateRealFunction {
+
+ /** Number of points. */
+ private static final short N = 4;
+
+ /** Coefficients */
+ private final double[][] a;
+
+ /** First partial derivative along x. */
+ private BivariateRealFunction partialDerivativeX;
+
+ /** First partial derivative along y. */
+ private BivariateRealFunction partialDerivativeY;
+
+ /** Second partial derivative along x. */
+ private BivariateRealFunction partialDerivativeXX;
+
+ /** Second partial derivative along y. */
+ private BivariateRealFunction partialDerivativeYY;
+
+ /** Second crossed partial derivative. */
+ private BivariateRealFunction partialDerivativeXY;
+
+ /**
+ * Simple constructor.
+ * @param a Spline coefficients
+ */
+ public BicubicSplineFunction(double[] a) {
+ this.a = new double[N][N];
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ this.a[i][j] = a[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++) {
+ 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 BivariateRealFunction partialDerivativeX() {
+ if (partialDerivativeX == null) {
+ computePartialDerivatives();
+ }
+
+ return partialDerivativeX;
+ }
+ /**
+ * @return the partial derivative wrt {@code y}.
+ */
+ public BivariateRealFunction partialDerivativeY() {
+ if (partialDerivativeY == null) {
+ computePartialDerivatives();
+ }
+
+ return partialDerivativeY;
+ }
+ /**
+ * @return the second partial derivative wrt {@code x}.
+ */
+ public BivariateRealFunction partialDerivativeXX() {
+ if (partialDerivativeXX == null) {
+ computePartialDerivatives();
+ }
+
+ return partialDerivativeXX;
+ }
+ /**
+ * @return the second partial derivative wrt {@code y}.
+ */
+ public BivariateRealFunction partialDerivativeYY() {
+ if (partialDerivativeYY == null) {
+ computePartialDerivatives();
+ }
+
+ return partialDerivativeYY;
+ }
+ /**
+ * @return the second partial cross-derivative.
+ */
+ public BivariateRealFunction partialDerivativeXY() {
+ if (partialDerivativeXY == null) {
+ computePartialDerivatives();
+ }
+
+ return partialDerivativeXY;
+ }
+
+ /**
+ * Compute all partial derivatives functions.
+ */
+ private void computePartialDerivatives() {
+ 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 BivariateRealFunction() {
+ 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 BivariateRealFunction() {
+ 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 BivariateRealFunction() {
+ 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 BivariateRealFunction() {
+ 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 BivariateRealFunction() {
+ 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);
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.java
new file mode 100644
index 0000000..42f73c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Generates a bicubic interpolating function.
+ *
+ * @version $Revision: 980944 $ $Date: 2010-07-30 22:31:11 +0200 (ven. 30 juil. 2010) $
+ * @since 2.2
+ */
+public class BicubicSplineInterpolator
+ implements BivariateRealGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws MathException, IllegalArgumentException {
+ 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);
+ }
+
+ MathUtils.checkOrder(xval);
+ MathUtils.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 UnivariateRealFunction 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 UnivariateRealFunction 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);
+ }
+
+ /**
+ * 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/math/analysis/interpolation/BivariateRealGridInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.java
new file mode 100644
index 0000000..218d328
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+
+/**
+ * Interface representing a bivariate real interpolating function where the
+ * sample points must be specified on a regular grid.
+ *
+ * @version $Revision: 936391 $ $Date: 2010-04-21 19:00:56 +0200 (mer. 21 avril 2010) $
+ */
+public interface BivariateRealGridInterpolator {
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @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 data set.
+ * @throws MathException if arguments violate assumptions made by the
+ * interpolation algorithm.
+ */
+ BivariateRealFunction interpolate(double[] xval, double[] yval, double[][] fval)
+ throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.java
new file mode 100644
index 0000000..9b80079
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolator.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.math.analysis.interpolation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.DuplicateSampleAbscissaException;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunctionLagrangeForm;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunctionNewtonForm;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 825919 $ $Date: 2009-10-16 16:51:55 +0200 (ven. 16 oct. 2009) $
+ * @since 1.2
+ */
+public class DividedDifferenceInterpolator implements UnivariateRealInterpolator,
+ Serializable {
+
+ /** serializable version identifier */
+ private static final long serialVersionUID = 107049519551235069L;
+
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @param x the interpolating points array
+ * @param y the interpolating values array
+ * @return a function which interpolates the data set
+ * @throws DuplicateSampleAbscissaException if arguments are invalid
+ */
+ public PolynomialFunctionNewtonForm interpolate(double x[], double y[]) throws
+ DuplicateSampleAbscissaException {
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ }
+
+ /**
+ * Returns 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>
+ * <p>
+ * The computational complexity is O(N^2).</p>
+ *
+ * @param x the interpolating points array
+ * @param y the interpolating values array
+ * @return a fresh copy of the divided difference array
+ * @throws DuplicateSampleAbscissaException if any abscissas coincide
+ */
+ protected static double[] computeDividedDifference(final double x[], final double y[])
+ throws DuplicateSampleAbscissaException {
+
+ PolynomialFunctionLagrangeForm.verifyInterpolationArray(x, y);
+
+ 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];
+ if (denominator == 0.0) {
+ // This happens only when two abscissas are identical.
+ throw new DuplicateSampleAbscissaException(x[j], j, j+i);
+ }
+ divdiff[j] = (divdiff[j+1] - divdiff[j]) / denominator;
+ }
+ a[i] = divdiff[0];
+ }
+
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.java
new file mode 100644
index 0000000..71ab9a9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/LinearInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Implements a linear function for interpolation of real univariate functions.
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public class LinearInterpolator implements UnivariateRealInterpolator {
+ /**
+ * 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 org.apache.commons.math.exception.NonMonotonousSequenceException
+ * 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[]) {
+ 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;
+
+ MathUtils.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]);
+ }
+
+ 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/math/analysis/interpolation/LoessInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java
new file mode 100644
index 0000000..5f00e14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/LoessInterpolator.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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://www.math.tau.ac.il/~yekutiel/MA seminar/Cleveland 1979.pdf">
+ * 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 to build a spline on the obtained loess fit.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class LoessInterpolator
+ implements UnivariateRealInterpolator, 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.
+ */
+ 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.
+ */
+ 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;
+ }
+
+ /**
+ * Constructs 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.</br>
+ * 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.</br>
+ * A sensible value is usually 0 (just the initial fit without any
+ * robustness iterations) to 4, the default value is
+ * {@link #DEFAULT_ROBUSTNESS_ITERS}.
+ * @throws MathException if bandwidth does not lie in the interval [0,1]
+ * or if robustnessIters is negative.
+ * @see #LoessInterpolator(double, int, double)
+ */
+ public LoessInterpolator(double bandwidth, int robustnessIters) throws MathException {
+ this(bandwidth, robustnessIters, DEFAULT_ACCURACY);
+ }
+
+ /**
+ * Constructs 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.</br>
+ * 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.</br>
+ * 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 MathException if bandwidth does not lie in the interval [0,1]
+ * or if robustnessIters is negative.
+ * @see #LoessInterpolator(double, int)
+ * @since 2.1
+ */
+ public LoessInterpolator(double bandwidth, int robustnessIters, double accuracy) throws MathException {
+ if (bandwidth < 0 || bandwidth > 1) {
+ throw new MathException(LocalizedFormats.BANDWIDTH_OUT_OF_INTERVAL,
+ bandwidth);
+ }
+ this.bandwidth = bandwidth;
+ if (robustnessIters < 0) {
+ throw new MathException(LocalizedFormats.NEGATIVE_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.math.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 MathException if some of the following conditions are false:
+ * <ul>
+ * <li> Arguments and values are of the same size that is greater than zero</li>
+ * <li> The arguments are in a strictly increasing order</li>
+ * <li> All arguments and values are finite real numbers</li>
+ * </ul>
+ */
+ public final PolynomialSplineFunction interpolate(
+ final double[] xval, final double[] yval) throws MathException {
+ return new SplineInterpolator().interpolate(xval, smooth(xval, yval));
+ }
+
+ /**
+ * Compute a weighted 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
+ * @param weights point weights: coefficients by which the robustness weight of a point is multiplied
+ * @return values of the loess fit at corresponding original abscissae
+ * @throws MathException if some of the following conditions are false:
+ * <ul>
+ * <li> Arguments and values are of the same size that is greater than zero</li>
+ * <li> The arguments are in a strictly increasing order</li>
+ * <li> All arguments and values are finite real numbers</li>
+ * </ul>
+ * @since 2.1
+ */
+ public final double[] smooth(final double[] xval, final double[] yval, final double[] weights)
+ throws MathException {
+ if (xval.length != yval.length) {
+ throw new MathException(LocalizedFormats.MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS,
+ xval.length, yval.length);
+ }
+
+ final int n = xval.length;
+
+ if (n == 0) {
+ throw new MathException(LocalizedFormats.LOESS_EXPECTS_AT_LEAST_ONE_POINT);
+ }
+
+ checkAllFiniteReal(xval, LocalizedFormats.NON_REAL_FINITE_ABSCISSA);
+ checkAllFiniteReal(yval, LocalizedFormats.NON_REAL_FINITE_ORDINATE);
+ checkAllFiniteReal(weights, LocalizedFormats.NON_REAL_FINITE_WEIGHT);
+
+ checkStrictlyIncreasing(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 MathException(LocalizedFormats.TOO_SMALL_BANDWIDTH,
+ n, 2.0 / n, bandwidth);
+ }
+
+ 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 MathException if some of the following conditions are false:
+ * <ul>
+ * <li> Arguments and values are of the same size that is greater than zero</li>
+ * <li> The arguments are in a strictly increasing order</li>
+ * <li> All arguments and values are finite real numbers</li>
+ * </ul>
+ */
+ public final double[] smooth(final double[] xval, final double[] yval)
+ throws MathException {
+ if (xval.length != yval.length) {
+ throw new MathException(LocalizedFormats.MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS,
+ 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 xval[i-1], update the interval so that it embraces
+ * the same number of points closest to xval[i], ignoring zero weights.
+ *
+ * @param xval arguments array
+ * @param weights weights array
+ * @param i the index around which the new interval should be computed
+ * @param bandwidthInterval a two-element array {left, right} such that: <p/>
+ * <tt>(left==0 or xval[i] - xval[left-1] > xval[right] - xval[i])</tt>
+ * <p/> and also <p/>
+ * <tt>(right==xval.length-1 or xval[right+1] - xval[i] > xval[i] - xval[left])</tt>.
+ * 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;
+ }
+ }
+
+ /**
+ * Returns the smallest index j such that j > i && (j==weights.length || weights[j] != 0)
+ * @param weights weights array
+ * @param i the index from which to start search; must be < weights.length
+ * @return the smallest index j such that j > i && (j==weights.length || weights[j] != 0)
+ */
+ 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 the argument
+ * @return (1-|x|^3)^3
+ */
+ private static double tricube(final double x) {
+ final double tmp = 1 - x * x * x;
+ return tmp * tmp * tmp;
+ }
+
+ /**
+ * Check that all elements of an array are finite real numbers.
+ *
+ * @param values the values array
+ * @param pattern pattern of the error message
+ * @throws MathException if one of the values is not a finite real number
+ */
+ private static void checkAllFiniteReal(final double[] values, final Localizable pattern)
+ throws MathException {
+ for (int i = 0; i < values.length; i++) {
+ final double x = values[i];
+ if (Double.isInfinite(x) || Double.isNaN(x)) {
+ throw new MathException(pattern, i, x);
+ }
+ }
+ }
+
+ /**
+ * Check that elements of the abscissae array are in a strictly
+ * increasing order.
+ *
+ * @param xval the abscissae array
+ * @throws MathException if the abscissae array
+ * is not in a strictly increasing order
+ */
+ private static void checkStrictlyIncreasing(final double[] xval)
+ throws MathException {
+ for (int i = 0; i < xval.length; ++i) {
+ if (i >= 1 && xval[i - 1] >= xval[i]) {
+ throw new MathException(LocalizedFormats.OUT_OF_ORDER_ABSCISSA_ARRAY,
+ i - 1, xval[i - 1], i, xval[i]);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.java
new file mode 100644
index 0000000..a710e82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatingFunction.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.math.analysis.interpolation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.linear.ArrayRealVector;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.random.UnitSphereRandomVectorGenerator;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Interpolating function that implements the
+ * <a href="http://www.dudziak.com/microsphere.php">Microsphere Projection</a>.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class MicrosphereInterpolatingFunction
+ implements MultivariateRealFunction {
+ /**
+ * 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 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 dimension} is thus the dimension of the sampled
+ * space).
+ * @param yval the 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 are no data (xval null or zero length)
+ */
+ public MicrosphereInterpolatingFunction(double[][] xval,
+ double[] yval,
+ int brightnessExponent,
+ int microsphereElements,
+ UnitSphereRandomVectorGenerator rand)
+ throws DimensionMismatchException, NoDataException {
+ if (xval.length == 0 || xval[0] == null) {
+ throw new NoDataException();
+ }
+
+ if (xval.length != yval.length) {
+ throw new DimensionMismatchException(xval.length, yval.length);
+ }
+
+ 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.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.
+ */
+ public double value(double[] point) {
+
+ 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 cosine of the angle
+ */
+ 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/math/analysis/interpolation/MicrosphereInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.java
new file mode 100644
index 0000000..c2a4009
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.exception.NotPositiveException;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.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
+ *
+ * @version $Revision: 980944 $ $Date: 2010-07-30 22:31:11 +0200 (ven. 30 juil. 2010) $
+ */
+public class MicrosphereInterpolator
+ implements MultivariateRealInterpolator {
+
+ /**
+ * 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 int microsphereElements;
+
+ /**
+ * Exponent used in the power law that computes the weights of the
+ * sample data.
+ */
+ private int brightnessExponent;
+
+ /** Create a microsphere interpolator with default settings.
+ * <p>Calling this constructor is equivalent to call {@link
+ * #MicrosphereInterpolator(int, int)
+ * MicrosphereInterpolator(MicrosphereInterpolator.DEFAULT_MICROSPHERE_ELEMENTS,
+ * MicrosphereInterpolator.DEFAULT_BRIGHTNESS_EXPONENT)}.</p>
+ */
+ public MicrosphereInterpolator() {
+ this(DEFAULT_MICROSPHERE_ELEMENTS, DEFAULT_BRIGHTNESS_EXPONENT);
+ }
+
+ /** Create a microsphere interpolator.
+ * @param microsphereElements number of surface elements of the microsphere.
+ * @param brightnessExponent exponent used in the power law that computes the
+ * weights of the sample data.
+ * @throws NotPositiveException if {@code microsphereElements <= 0}
+ * or {@code brightnessExponent < 0}.
+ */
+ public MicrosphereInterpolator(final int microsphereElements,
+ final int brightnessExponent) {
+ setMicropshereElements(microsphereElements);
+ setBrightnessExponent(brightnessExponent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MultivariateRealFunction interpolate(final double[][] xval,
+ final double[] yval)
+ throws MathException, IllegalArgumentException {
+ final UnitSphereRandomVectorGenerator rand
+ = new UnitSphereRandomVectorGenerator(xval[0].length);
+ return new MicrosphereInterpolatingFunction(xval, yval,
+ brightnessExponent,
+ microsphereElements,
+ rand);
+ }
+
+ /**
+ * Set the brightness exponent.
+ * @param exponent Exponent for computing the distance dimming
+ * factor.
+ * @throws NotPositiveException if {@code exponent < 0}.
+ */
+ public void setBrightnessExponent(final int exponent) {
+ if (exponent < 0) {
+ throw new NotPositiveException(exponent);
+ }
+ brightnessExponent = exponent;
+ }
+
+ /**
+ * Set the number of microsphere elements.
+ * @param elements Number of surface elements of the microsphere.
+ * @throws NotStrictlyPositiveException if {@code elements <= 0}.
+ */
+ public void setMicropshereElements(final int elements) {
+ if (elements <= 0) {
+ throw new NotStrictlyPositiveException(elements);
+ }
+ microsphereElements = elements;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.java
new file mode 100644
index 0000000..ed7690c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/MultivariateRealInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+
+/**
+ * Interface representing a univariate real interpolating function.
+ *
+ * @since 2.1
+ * @version $Revision: 924794 $ $Date: 2010-03-18 15:15:50 +0100 (jeu. 18 mars 2010) $
+ */
+public interface MultivariateRealInterpolator {
+
+ /**
+ * 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 MathException if arguments violate assumptions made by the
+ * interpolation algorithm or some dimension mismatch occurs
+ * @throws IllegalArgumentException if there are no data (xval null or zero length)
+ */
+ MultivariateRealFunction interpolate(double[][] xval, double[] yval)
+ throws MathException, IllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.java
new file mode 100644
index 0000000..27e89cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolator.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.math.analysis.interpolation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunctionLagrangeForm;
+
+/**
+ * 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 evalution is in PolynomialFunctionLagrangeForm,
+ * this class provides an easy-to-use interface to it.</p>
+ *
+ * @version $Revision: 799857 $ $Date: 2009-08-01 15:07:12 +0200 (sam. 01 août 2009) $
+ * @since 1.2
+ */
+public class NevilleInterpolator implements UnivariateRealInterpolator,
+ Serializable {
+
+ /** serializable version identifier */
+ static final long serialVersionUID = 3003707660147873733L;
+
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @param x the interpolating points array
+ * @param y the interpolating values array
+ * @return a function which interpolates the data set
+ * @throws MathException if arguments are invalid
+ */
+ public PolynomialFunctionLagrangeForm interpolate(double x[], double y[])
+ throws MathException {
+ return new PolynomialFunctionLagrangeForm(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.java
new file mode 100644
index 0000000..5514433
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.MathUtils.OrderDirection;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Generates a bicubic interpolation function.
+ * Before interpolating, smoothing of the input data is performed using
+ * splines.
+ * See <b>Handbook on splines for the user</b>, ISBN 084939404X,
+ * chapter 2.
+ *
+ * @version $Revision: 1059400 $ $Date: 2011-01-15 20:35:27 +0100 (sam. 15 janv. 2011) $
+ * @since 2.1
+ * @deprecated This class does not perform smoothing; the name is thus misleading.
+ * Please use {@link org.apache.commons.math.analysis.interpolation.BicubicSplineInterpolator}
+ * instead. If smoothing is desired, a tentative implementation is provided in class
+ * {@link org.apache.commons.math.analysis.interpolation.SmoothingPolynomialBicubicSplineInterpolator}.
+ * This class will be removed in math 3.0.
+ */
+@Deprecated
+public class SmoothingBicubicSplineInterpolator
+ implements BivariateRealGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public BivariateRealFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] zval)
+ throws MathException, IllegalArgumentException {
+ if (xval.length == 0 || yval.length == 0 || zval.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.NO_DATA);
+ }
+ if (xval.length != zval.length) {
+ throw new DimensionMismatchException(xval.length, zval.length);
+ }
+
+ MathUtils.checkOrder(xval, OrderDirection.INCREASING, true);
+ MathUtils.checkOrder(yval, OrderDirection.INCREASING, true);
+
+ 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
+ // zX[j][i] = f(xval[i], yval[j])
+ final double[][] zX = new double[yLen][xLen];
+ for (int i = 0; i < xLen; i++) {
+ if (zval[i].length != yLen) {
+ throw new DimensionMismatchException(zval[i].length, yLen);
+ }
+
+ for (int j = 0; j < yLen; j++) {
+ zX[j][i] = zval[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, zX[j]);
+ }
+
+ // For every knot (xval[i], yval[j]) of the grid, calculate corrected
+ // values zY_1
+ final double[][] zY_1 = new double[xLen][yLen];
+ for (int j = 0; j < yLen; j++) {
+ final PolynomialSplineFunction f = ySplineX[j];
+ for (int i = 0; i < xLen; i++) {
+ zY_1[i][j] = f.value(xval[i]);
+ }
+ }
+
+ // For each line x[i] (0 <= i < xLen), construct a 1D spline with
+ // respect to variable y generated by array zY_1[i]
+ final PolynomialSplineFunction[] xSplineY = new PolynomialSplineFunction[xLen];
+ for (int i = 0; i < xLen; i++) {
+ xSplineY[i] = spInterpolator.interpolate(yval, zY_1[i]);
+ }
+
+ // For every knot (xval[i], yval[j]) of the grid, calculate corrected
+ // values zY_2
+ final double[][] zY_2 = new double[xLen][yLen];
+ for (int i = 0; i < xLen; i++) {
+ final PolynomialSplineFunction f = xSplineY[i];
+ for (int j = 0; j < yLen; j++) {
+ zY_2[i][j] = f.value(yval[j]);
+ }
+ }
+
+ // Partial derivatives with respect to x at the grid knots
+ final double[][] dZdX = new double[xLen][yLen];
+ for (int j = 0; j < yLen; j++) {
+ final UnivariateRealFunction f = ySplineX[j].derivative();
+ for (int i = 0; i < xLen; i++) {
+ dZdX[i][j] = f.value(xval[i]);
+ }
+ }
+
+ // Partial derivatives with respect to y at the grid knots
+ final double[][] dZdY = new double[xLen][yLen];
+ for (int i = 0; i < xLen; i++) {
+ final UnivariateRealFunction f = xSplineY[i].derivative();
+ for (int j = 0; j < yLen; j++) {
+ dZdY[i][j] = f.value(yval[j]);
+ }
+ }
+
+ // Cross partial derivatives
+ final double[][] dZdXdY = 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);
+ dZdXdY[i][j] = (zY_2[nI][nJ] - zY_2[nI][pJ] -
+ zY_2[pI][nJ] + zY_2[pI][pJ]) /
+ ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ]));
+ }
+ }
+
+ // Create the interpolating splines
+ return new BicubicSplineInterpolatingFunction(xval, yval, zY_2,
+ dZdX, dZdY, dZdXdY);
+ }
+
+ /**
+ * 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/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
new file mode 100644
index 0000000..406d603
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
+import org.apache.commons.math.optimization.fitting.PolynomialFitter;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+
+/**
+ * Generates a bicubic interpolation function.
+ * Prior to generating the interpolating function, the input is smoothed using
+ * polynomial fitting.
+ *
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @since 2.2
+ */
+public class SmoothingPolynomialBicubicSplineInterpolator
+ extends BicubicSplineInterpolator {
+
+ /** Fitter for x. */
+ private final PolynomialFitter xFitter;
+
+ /** Fitter for y. */
+ private final PolynomialFitter yFitter;
+
+ /**
+ * Default constructor. The degree of the fitting polynomials is set to 3.
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator() {
+ this(3);
+ }
+
+ /**
+ * @param degree Degree of the polynomial fitting functions.
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator(int degree) {
+ 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.
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator(int xDegree,
+ int yDegree) {
+ xFitter = new PolynomialFitter(xDegree, new GaussNewtonOptimizer(false));
+ yFitter = new PolynomialFitter(yDegree, new GaussNewtonOptimizer(false));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws MathException {
+ 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);
+ }
+ }
+
+ MathUtils.checkOrder(xval);
+ MathUtils.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]);
+ }
+
+ yPolyX[j] = xFitter.fit();
+ }
+
+ // 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]);
+ }
+
+ xPolyY[i] = yFitter.fit();
+ }
+
+ // 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/math/analysis/interpolation/SplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/SplineInterpolator.java
new file mode 100644
index 0000000..f25ba83
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.analysis.interpolation;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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,
+ * x[0] < x[i] ... < x[n]. The x values are referred to as "knot points."</p>
+ * <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>
+ * <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>
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ *
+ */
+public class SplineInterpolator implements UnivariateRealInterpolator {
+
+ /**
+ * 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 org.apache.commons.math.exception.NonMonotonousSequenceException
+ * 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[]) {
+ 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.
+ int n = x.length - 1;
+
+ MathUtils.checkOrder(x);
+
+ // Differences between knot points
+ double h[] = new double[n];
+ for (int i = 0; i < n; i++) {
+ h[i] = x[i + 1] - x[i];
+ }
+
+ double mu[] = new double[n];
+ 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)
+ double b[] = new double[n];
+ double c[] = new double[n + 1];
+ 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]);
+ }
+
+ PolynomialFunction polynomials[] = new PolynomialFunction[n];
+ 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/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..ea435a3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.java
@@ -0,0 +1,483 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import org.apache.commons.math.analysis.TrivariateRealFunction;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Tricubic_interpolation">
+ * tricubic spline interpolation</a>, as proposed in
+ * <quote>
+ * Tricubic interpolation in three dimensions<br/>
+ * F. Lekien and J. Marsden<br/>
+ * <em>Int. J. Numer. Meth. Engng</em> 2005; <b>63</b>:455-471
+ * </quote>
+ *
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public class TricubicSplineInterpolatingFunction
+ implements TrivariateRealFunction {
+ /**
+ * 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 IllegalArgumentException 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) {
+ 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);
+ }
+
+ MathUtils.checkOrder(x);
+ MathUtils.checkOrder(y);
+ MathUtils.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}
+ */
+ public double value(double x, double y, double z) {
+ 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.
+ *
+ * @version $Revision$ $Date$
+ */
+class TricubicSplineFunction
+ implements TrivariateRealFunction {
+ /** 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.
+ */
+ public 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.
+ */
+ public double value(double x, double y, double z) {
+ 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/math/analysis/interpolation/TricubicSplineInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.java
new file mode 100644
index 0000000..dac7f26
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Generates a tricubic interpolating function.
+ *
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public class TricubicSplineInterpolator
+ implements TrivariateRealGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public TricubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[] zval,
+ final double[][][] fval)
+ throws MathException {
+ 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);
+ }
+
+ MathUtils.checkOrder(xval);
+ MathUtils.checkOrder(yval);
+ MathUtils.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();
+
+ // 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/math/analysis/interpolation/TrivariateRealGridInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.java
new file mode 100644
index 0000000..c42d7aa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.TrivariateRealFunction;
+
+/**
+ * Interface representing a trivariate real interpolating function where the
+ * sample points must be specified on a regular grid.
+ *
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public interface TrivariateRealGridInterpolator {
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @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 org.apache.commons.math.exception.NoDataException if any of the arrays has zero length.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException if the array lengths are inconsistent.
+ * @throws MathException if arguments violate assumptions made by the
+ * interpolation algorithm.
+ */
+ TrivariateRealFunction interpolate(double[] xval, double[] yval, double[] zval, double[][][] fval)
+ throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java b/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.java
new file mode 100644
index 0000000..09f5487
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.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.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+/**
+ * Interface representing a univariate real interpolating function.
+ *
+ * @version $Revision: 821626 $ $Date: 2009-10-04 23:57:30 +0200 (dim. 04 oct. 2009) $
+ */
+public interface UnivariateRealInterpolator {
+
+ /**
+ * Computes an interpolating function for the data set.
+ * @param xval the arguments for the interpolation points
+ * @param yval the values for the interpolation points
+ * @return a function which interpolates the data set
+ * @throws MathException if arguments violate assumptions made by the
+ * interpolation algorithm
+ */
+ UnivariateRealFunction interpolate(double xval[], double yval[])
+ throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/interpolation/package.html b/src/main/java/org/apache/commons/math/analysis/interpolation/package.html
new file mode 100644
index 0000000..136c576
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/interpolation/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Univariate real functions interpolation algorithms.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/analysis/package.html b/src/main/java/org/apache/commons/math/analysis/package.html
new file mode 100644
index 0000000..63b64a8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/package.html
@@ -0,0 +1,33 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ <p>
+ Parent package for common numerical analysis procedures, including root finding,
+ function interpolation and integration. Note that the optimization (i.e. minimization
+ and maximization) is a huge separate top package, despite it also operate on functions
+ as defined by this top-level package.
+ </p>
+ <p>
+ Functions interfaces are intended to be implemented by user code to represent their
+ domain problems. The algorithms provided by the library will then operate on these
+ function 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.
+ </p>
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java
new file mode 100644
index 0000000..ec528b4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunction.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.polynomials;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+public class PolynomialFunction implements DifferentiableUnivariateRealFunction, 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 NullPointerException if c is null
+ * @throws NoDataException if c is empty
+ */
+ public PolynomialFunction(double c[]) {
+ super();
+ 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 <br>
+ * <code>coefficients[n] * x^n + ... + coefficients[1] * x + coefficients[0]</code>
+ * </p>
+ *
+ * @param x the argument for which the function value should be computed
+ * @return the value of the polynomial at the given point
+ * @see UnivariateRealFunction#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 the coefficients of the polynomial to evaluate
+ * @param argument the input value
+ * @return the value of the polynomial
+ * @throws NoDataException if coefficients is empty
+ * @throws NullPointerException if coefficients is null
+ */
+ protected static double evaluate(double[] coefficients, double argument) {
+ 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;
+ }
+
+ /**
+ * Add a polynomial to the instance.
+ * @param p polynomial to add
+ * @return a new polynomial which is the sum of the instance and 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 difference the instance minus 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
+ */
+ 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
+ */
+ 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 the coefficients of the polynomial to differentiate
+ * @return the coefficients of the derivative or null if coefficients has length 1.
+ * @throws NoDataException if coefficients is empty
+ * @throws NullPointerException if coefficients is null
+ */
+ protected static double[] differentiate(double[] 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 PolynomialRealFunction
+ *
+ * @return the derivative polynomial
+ */
+ public PolynomialFunction polynomialDerivative() {
+ return new PolynomialFunction(differentiate(coefficients));
+ }
+
+ /**
+ * Returns the derivative as a UnivariateRealFunction
+ *
+ * @return the derivative function
+ */
+ public UnivariateRealFunction 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(Double.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(Double.toString(absAi));
+ s.append(' ');
+ }
+
+ s.append("x");
+ if (i > 1) {
+ s.append('^');
+ s.append(Integer.toString(i));
+ }
+ }
+
+ }
+
+ return s.toString();
+
+ }
+
+ /** {@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;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java
new file mode 100644
index 0000000..b40bf60
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeForm.java
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.polynomials;
+
+import org.apache.commons.math.DuplicateSampleAbscissaException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ * @since 1.2
+ */
+public class PolynomialFunctionLagrangeForm implements UnivariateRealFunction {
+
+ /**
+ * 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 IllegalArgumentException if input arrays are not valid
+ */
+ public PolynomialFunctionLagrangeForm(double x[], double y[])
+ throws IllegalArgumentException {
+
+ verifyInterpolationArray(x, y);
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double z) throws FunctionEvaluationException {
+ try {
+ return evaluate(x, y, z);
+ } catch (DuplicateSampleAbscissaException e) {
+ throw new FunctionEvaluationException(z, e.getSpecificPattern(), e.getGeneralPattern(), e.getArguments());
+ }
+ }
+
+ /**
+ * 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.
+ * <p>
+ * This function is made public static so that users can call it directly
+ * without instantiating PolynomialFunctionLagrangeForm object.</p>
+ *
+ * @param x the interpolating points array
+ * @param y the interpolating values array
+ * @param z the point at which the function value is to be computed
+ * @return the function value
+ * @throws DuplicateSampleAbscissaException if the sample has duplicate abscissas
+ * @throws IllegalArgumentException if inputs are not valid
+ */
+ public static double evaluate(double x[], double y[], double z) throws
+ DuplicateSampleAbscissaException, IllegalArgumentException {
+
+ verifyInterpolationArray(x, y);
+
+ 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];
+ if (divider == 0.0) {
+ // This happens only when two abscissas are identical.
+ throw new DuplicateSampleAbscissaException(x[i], i, 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.
+ * <p>
+ * Note this computation can be ill-conditioned. Use with caution
+ * and only when it is necessary.</p>
+ *
+ * @throws ArithmeticException if any abscissas coincide
+ */
+ protected void computeCoefficients() throws ArithmeticException {
+
+ 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];
+ }
+ }
+ if (d == 0.0) {
+ // This happens only when two abscissas are identical.
+ for (int k = 0; k < n; ++k) {
+ if ((i != k) && (x[i] == x[k])) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.IDENTICAL_ABSCISSAS_DIVISION_BY_ZERO,
+ i, k, x[i]);
+ }
+ }
+ }
+ 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;
+ }
+
+ /**
+ * Verifies that the interpolation arrays are valid.
+ * <p>
+ * The arrays features checked by this method are that both arrays have the
+ * same length and this length is at least 2.
+ * </p>
+ * <p>
+ * The interpolating points must be distinct. However it is not
+ * verified here, it is checked in evaluate() and computeCoefficients().
+ * </p>
+ *
+ * @param x the interpolating points array
+ * @param y the interpolating values array
+ * @throws IllegalArgumentException if not valid
+ * @see #evaluate(double[], double[], double)
+ * @see #computeCoefficients()
+ */
+ public static void verifyInterpolationArray(double x[], double y[])
+ throws IllegalArgumentException {
+
+ if (x.length != y.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, x.length, y.length);
+ }
+
+ if (x.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.WRONG_NUMBER_OF_POINTS, 2, x.length);
+ }
+
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.java
new file mode 100644
index 0000000..5ee3224
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonForm.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.math.analysis.polynomials;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ * @since 1.2
+ */
+public class PolynomialFunctionNewtonForm implements UnivariateRealFunction {
+
+ /**
+ * 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 the coefficients in Newton form formula
+ * @param c the centers
+ * @throws IllegalArgumentException if input arrays are not valid
+ */
+ public PolynomialFunctionNewtonForm(double a[], double c[])
+ throws IllegalArgumentException {
+
+ 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 the point at which the function value is to be computed
+ * @return the function value
+ * @throws FunctionEvaluationException if a runtime error occurs
+ * @see UnivariateRealFunction#value(double)
+ */
+ public double value(double z) throws FunctionEvaluationException {
+ return evaluate(a, c, z);
+ }
+
+ /**
+ * 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 the coefficients in Newton form formula
+ * @param c the centers
+ * @param z the point at which the function value is to be computed
+ * @return the function value
+ * @throws FunctionEvaluationException if a runtime error occurs
+ * @throws IllegalArgumentException if inputs are not valid
+ */
+ public static double evaluate(double a[], double c[], double z)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ verifyInputArray(a, c);
+
+ 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 IllegalArgumentException if not valid
+ * @see org.apache.commons.math.analysis.interpolation.DividedDifferenceInterpolator#computeDividedDifference(double[],
+ * double[])
+ */
+ protected static void verifyInputArray(double a[], double c[]) throws
+ IllegalArgumentException {
+
+ if (a.length < 1 || c.length < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ if (a.length != c.length + 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1,
+ a.length, c.length);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.java
new file mode 100644
index 0000000..a0e1e01
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunction.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.math.analysis.polynomials;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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 <br>
+ * <code>polynomials[j](x - knot[j])</code></li></ol></p>
+ *
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class PolynomialSplineFunction
+ implements DifferentiableUnivariateRealFunction {
+
+ /** 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])</code> where i is the
+ * knot segment to which x belongs.
+ */
+ private final PolynomialFunction polynomials[];
+
+ /**
+ * Number of spline segments = number of polynomials
+ * = number of partition points - 1
+ */
+ private final int n;
+
+
+ /**
+ * Construct a polynomial spline function with the given segment delimiters
+ * and interpolating polynomials.
+ * <p>
+ * The constructor copies both arrays and assigns the copies to the knots
+ * and polynomials properties, respectively.</p>
+ *
+ * @param knots spline segment interval delimiters
+ * @param polynomials polynomial functions that make up the spline
+ * @throws NullPointerException if either of the input arrays is null
+ * @throws IllegalArgumentException if knots has length less than 2,
+ * <code>polynomials.length != knots.length - 1 </code>, or the knots array
+ * is not strictly increasing.
+ *
+ */
+ public PolynomialSplineFunction(double knots[], PolynomialFunction polynomials[]) {
+ if (knots.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION,
+ 2, knots.length);
+ }
+ if (knots.length - 1 != polynomials.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POLYNOMIAL_INTERPOLANTS_MISMATCH_SEGMENTS,
+ polynomials.length, knots.length);
+ }
+ if (!isStrictlyIncreasing(knots)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_STRICTLY_INCREASING_KNOT_VALUES);
+ }
+
+ 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.</p>
+ *
+ * @param v the point for which the function value should be computed
+ * @return the value
+ * @throws ArgumentOutsideDomainException if v is outside of the domain of
+ * of the spline function (less than the smallest knot point or greater
+ * than the largest knot point)
+ */
+ public double value(double v) throws ArgumentOutsideDomainException {
+ if (v < knots[0] || v > knots[n]) {
+ throw new ArgumentOutsideDomainException(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]);
+ }
+
+ /**
+ * Returns the derivative of the polynomial spline function as a UnivariateRealFunction
+ * @return the derivative function
+ */
+ public UnivariateRealFunction derivative() {
+ return polynomialSplineDerivative();
+ }
+
+ /**
+ * Returns the derivative of the polynomial spline function as a PolynomialSplineFunction
+ *
+ * @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);
+ }
+
+ /**
+ * Returns the number of spline segments = the number of polynomials
+ * = the number of knot points - 1.
+ *
+ * @return the number of spline segments
+ */
+ public int getN() {
+ return n;
+ }
+
+ /**
+ * Returns a copy of the interpolating polynomials array.
+ * <p>
+ * Returns a fresh copy of the array. Changes made to the copy will
+ * not affect the polynomials property.</p>
+ *
+ * @return the interpolating polynomials
+ */
+ public PolynomialFunction[] getPolynomials() {
+ PolynomialFunction p[] = new PolynomialFunction[n];
+ System.arraycopy(polynomials, 0, p, 0, n);
+ return p;
+ }
+
+ /**
+ * Returns an array copy of the knot points.
+ * <p>
+ * Returns a fresh copy of the array. Changes made to the copy
+ * will not affect the knots property.</p>
+ *
+ * @return the knot points
+ */
+ public double[] getKnots() {
+ double out[] = new double[n + 1];
+ System.arraycopy(knots, 0, out, 0, n + 1);
+ return out;
+ }
+
+ /**
+ * Determines if the given array is ordered in a strictly increasing
+ * fashion.
+ *
+ * @param x the array to examine.
+ * @return <code>true</code> if the elements in <code>x</code> are ordered
+ * in a stricly increasing manner. <code>false</code>, otherwise.
+ */
+ private static boolean isStrictlyIncreasing(double[] x) {
+ for (int i = 1; i < x.length; ++i) {
+ if (x[i - 1] >= x[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.java
new file mode 100644
index 0000000..5e939c7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.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.math.analysis.polynomials;
+
+import java.util.ArrayList;
+
+import org.apache.commons.math.fraction.BigFraction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * A collection of static methods that operate on or return polynomials.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class PolynomialsUtils {
+
+ /** Coefficients for Chebyshev polynomials. */
+ private static final ArrayList<BigFraction> CHEBYSHEV_COEFFICIENTS;
+
+ /** Coefficients for Hermite polynomials. */
+ private static final ArrayList<BigFraction> HERMITE_COEFFICIENTS;
+
+ /** Coefficients for Laguerre polynomials. */
+ private static final ArrayList<BigFraction> LAGUERRE_COEFFICIENTS;
+
+ /** Coefficients for Legendre polynomials. */
+ private static final ArrayList<BigFraction> LEGENDRE_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);
+
+ }
+
+ /**
+ * Private constructor, to prevent instantiation.
+ */
+ private PolynomialsUtils() {
+ }
+
+ /**
+ * Create a Chebyshev polynomial of the first kind.
+ * <p><a href="http://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html">Chebyshev
+ * polynomials of the first kind</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:
+ * <pre>
+ * T<sub>0</sub>(X) = 1
+ * T<sub>1</sub>(X) = X
+ * T<sub>k+1</sub>(X) = 2X T<sub>k</sub>(X) - T<sub>k-1</sub>(X)
+ * </pre></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() {
+ 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:
+ * <pre>
+ * H<sub>0</sub>(X) = 1
+ * H<sub>1</sub>(X) = 2X
+ * H<sub>k+1</sub>(X) = 2X H<sub>k</sub>(X) - 2k H<sub>k-1</sub>(X)
+ * </pre></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:
+ * <pre>
+ * L<sub>0</sub>(X) = 1
+ * L<sub>1</sub>(X) = 1 - X
+ * (k+1) L<sub>k+1</sub>(X) = (2k + 1 - X) L<sub>k</sub>(X) - k L<sub>k-1</sub>(X)
+ * </pre></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:
+ * <pre>
+ * P<sub>0</sub>(X) = 1
+ * P<sub>1</sub>(X) = X
+ * (k+1) P<sub>k+1</sub>(X) = (2k+1) X P<sub>k</sub>(X) - k P<sub>k-1</sub>(X)
+ * </pre></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)};
+ }
+ });
+ }
+
+ /** 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 ArrayList<BigFraction> coefficients,
+ final RecurrenceCoefficientsGenerator generator) {
+
+ final int maxDegree = (int) FastMath.floor(FastMath.sqrt(2 * coefficients.size())) - 1;
+ synchronized (PolynomialsUtils.class) {
+ 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 ArrayList<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 static 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<sub>k+1</sub>(X) = (a[0] + a[1] X) P<sub>k</sub>(X) - a[2] P<sub>k-1</sub>(X)
+ */
+ BigFraction[] generate(int k);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/polynomials/package.html b/src/main/java/org/apache/commons/math/analysis/polynomials/package.html
new file mode 100644
index 0000000..63ca96a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/polynomials/package.html
@@ -0,0 +1,23 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Univariate real polynomials implementations, seen as differentiable
+ univariate real functions.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java
new file mode 100644
index 0000000..c9d3a63
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/BisectionSolver.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class BisectionSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Construct a solver for the given function.
+ *
+ * @param f function to solve.
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public BisectionSolver(UnivariateRealFunction f) {
+ super(f, 100, 1E-6);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ */
+ public BisectionSolver() {
+ super(100, 1E-6);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(double min, double max, double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(double min, double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f, double min, double max, double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f, double min, double max, double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(maxEval, f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f, double min, double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f, double min, double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ clearResult();
+ verifyInterval(min,max);
+ double m;
+ double fm;
+ double fmin;
+
+ int i = 0;
+ while (i < maximalIterationCount) {
+ m = UnivariateRealSolverUtils.midpoint(min, max);
+ fmin = f.value(min);
+ fm = f.value(m);
+
+ if (fm * fmin > 0.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 = UnivariateRealSolverUtils.midpoint(min, max);
+ setResult(m, i);
+ return m;
+ }
+ ++i;
+ }
+
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java
new file mode 100644
index 0000000..5aa2447
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/BrentSolver.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html">
+ * Brent algorithm</a> for finding zeros of real univariate functions.
+ * <p>
+ * The function should be continuous but not necessarily smooth.</p>
+ *
+ * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $
+ */
+public class BrentSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Default absolute accuracy
+ * @since 2.1
+ */
+ public static final double DEFAULT_ABSOLUTE_ACCURACY = 1E-6;
+
+ /** Default maximum number of iterations
+ * @since 2.1
+ */
+ public static final int DEFAULT_MAXIMUM_ITERATIONS = 100;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 7694577816772532779L;
+
+ /**
+ * Construct a solver for the given function.
+ *
+ * @param f function to solve.
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public BrentSolver(UnivariateRealFunction f) {
+ super(f, DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Construct a solver with default properties.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public BrentSolver() {
+ super(DEFAULT_MAXIMUM_ITERATIONS, DEFAULT_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Construct a solver with the given absolute accuracy.
+ *
+ * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver
+ * @since 2.1
+ */
+ public BrentSolver(double absoluteAccuracy) {
+ super(DEFAULT_MAXIMUM_ITERATIONS, absoluteAccuracy);
+ }
+
+ /**
+ * Contstruct a solver with the given maximum iterations and absolute accuracy.
+ *
+ * @param maximumIterations maximum number of iterations
+ * @param absoluteAccuracy lower bound for absolute accuracy of solutions returned by the solver
+ * @since 2.1
+ */
+ public BrentSolver(int maximumIterations, double absoluteAccuracy) {
+ super(maximumIterations, absoluteAccuracy);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(double min, double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(double min, double max, double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a zero in the given interval with an initial guess.
+ * <p>Throws <code>IllegalArgumentException</code> if the values of the
+ * function at the three points have the same sign (note that it is
+ * allowed to have endpoints with the same sign if the initial point has
+ * opposite sign function-wise).</p>
+ *
+ * @param f function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param initial the start value to use (must be set to min if no
+ * initial point is known).
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if initial is not between min and max
+ * (even if it <em>is</em> a root)
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ clearResult();
+ if ((initial < min) || (initial > max)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS,
+ min, initial, max);
+ }
+
+ // return the initial guess if it is good enough
+ double yInitial = f.value(initial);
+ if (FastMath.abs(yInitial) <= functionValueAccuracy) {
+ setResult(initial, 0);
+ return result;
+ }
+
+ // return the first endpoint if it is good enough
+ double yMin = f.value(min);
+ if (FastMath.abs(yMin) <= functionValueAccuracy) {
+ setResult(min, 0);
+ return result;
+ }
+
+ // reduce interval if min and initial bracket the root
+ if (yInitial * yMin < 0) {
+ return solve(f, min, yMin, initial, yInitial, min, yMin);
+ }
+
+ // return the second endpoint if it is good enough
+ double yMax = f.value(max);
+ if (FastMath.abs(yMax) <= functionValueAccuracy) {
+ setResult(max, 0);
+ return result;
+ }
+
+ // reduce interval if initial and max bracket the root
+ if (yInitial * yMax < 0) {
+ return solve(f, initial, yInitial, max, yMax, initial, yInitial);
+ }
+
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax);
+
+ }
+
+ /**
+ * Find a zero in the given interval with an initial guess.
+ * <p>Throws <code>IllegalArgumentException</code> if the values of the
+ * function at the three points have the same sign (note that it is
+ * allowed to have endpoints with the same sign if the initial point has
+ * opposite sign function-wise).</p>
+ *
+ * @param f function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param initial the start value to use (must be set to min if no
+ * initial point is known).
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if initial is not between min and max
+ * (even if it <em>is</em> a root)
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a zero in the given interval.
+ * <p>
+ * Requires that the values of the function at the endpoints have opposite
+ * signs. An <code>IllegalArgumentException</code> is thrown if this is not
+ * the case.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ clearResult();
+ verifyInterval(min, max);
+
+ double ret = Double.NaN;
+
+ double yMin = f.value(min);
+ double yMax = f.value(max);
+
+ // Verify bracketing
+ double sign = yMin * yMax;
+ if (sign > 0) {
+ // check if either value is close to a zero
+ if (FastMath.abs(yMin) <= functionValueAccuracy) {
+ setResult(min, 0);
+ ret = min;
+ } else if (FastMath.abs(yMax) <= functionValueAccuracy) {
+ setResult(max, 0);
+ ret = max;
+ } else {
+ // neither value is close to zero and min and max do not bracket root.
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, yMin, yMax);
+ }
+ } else if (sign < 0){
+ // solve using only the first endpoint as initial guess
+ ret = solve(f, min, yMin, max, yMax, min, yMin);
+ } else {
+ // either min or max is a root
+ if (yMin == 0.0) {
+ ret = min;
+ } else {
+ ret = max;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Find a zero in the given interval.
+ * <p>
+ * Requires that the values of the function at the endpoints have opposite
+ * signs. An <code>IllegalArgumentException</code> is thrown if this is not
+ * the case.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a zero starting search according to the three provided points.
+ * @param f the function to solve
+ * @param x0 old approximation for the root
+ * @param y0 function value at the approximation for the root
+ * @param x1 last calculated approximation for the root
+ * @param y1 function value at the last calculated approximation
+ * for the root
+ * @param x2 bracket point (must be set to x0 if no bracket point is
+ * known, this will force starting with linear interpolation)
+ * @param y2 function value at the bracket point.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ */
+ private double solve(final UnivariateRealFunction f,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ double delta = x1 - x0;
+ double oldDelta = delta;
+
+ int i = 0;
+ while (i < maximalIterationCount) {
+ if (FastMath.abs(y2) < FastMath.abs(y1)) {
+ // use the bracket point if is better than last approximation
+ x0 = x1;
+ x1 = x2;
+ x2 = x0;
+ y0 = y1;
+ y1 = y2;
+ y2 = y0;
+ }
+ if (FastMath.abs(y1) <= functionValueAccuracy) {
+ // Avoid division by very small values. Assume
+ // the iteration has converged (the problem may
+ // still be ill conditioned)
+ setResult(x1, i);
+ return result;
+ }
+ double dx = x2 - x1;
+ double tolerance =
+ FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy);
+ if (FastMath.abs(dx) <= tolerance) {
+ setResult(x1, i);
+ return result;
+ }
+ if ((FastMath.abs(oldDelta) < tolerance) ||
+ (FastMath.abs(y0) <= FastMath.abs(y1))) {
+ // Force bisection.
+ delta = 0.5 * dx;
+ oldDelta = delta;
+ } else {
+ double r3 = y1 / y0;
+ double p;
+ double p1;
+ // the equality test (x0 == x2) is intentional,
+ // it is part of the original Brent's method,
+ // it should NOT be replaced by proximity test
+ if (x0 == x2) {
+ // Linear interpolation.
+ p = dx * r3;
+ p1 = 1.0 - r3;
+ } else {
+ // Inverse quadratic interpolation.
+ double r1 = y0 / y2;
+ double r2 = y1 / y2;
+ p = r3 * (dx * r1 * (r1 - r2) - (x1 - x0) * (r2 - 1.0));
+ p1 = (r1 - 1.0) * (r2 - 1.0) * (r3 - 1.0);
+ }
+ if (p > 0.0) {
+ p1 = -p1;
+ } else {
+ p = -p;
+ }
+ if (2.0 * p >= 1.5 * dx * p1 - FastMath.abs(tolerance * p1) ||
+ p >= FastMath.abs(0.5 * oldDelta * p1)) {
+ // Inverse quadratic interpolation gives a value
+ // in the wrong direction, or progress is slow.
+ // Fall back to bisection.
+ delta = 0.5 * dx;
+ oldDelta = delta;
+ } else {
+ oldDelta = delta;
+ delta = p / p1;
+ }
+ }
+ // Save old X1, Y1
+ x0 = x1;
+ y0 = y1;
+ // Compute new X1, Y1
+ if (FastMath.abs(delta) > tolerance) {
+ x1 = x1 + delta;
+ } else if (dx > 0.0) {
+ x1 = x1 + 0.5 * tolerance;
+ } else if (dx <= 0.0) {
+ x1 = x1 - 0.5 * tolerance;
+ }
+ y1 = f.value(x1);
+ if ((y1 > 0) == (y2 > 0)) {
+ x2 = x0;
+ y2 = y0;
+ delta = x1 - x0;
+ oldDelta = delta;
+ }
+ i++;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.java
new file mode 100644
index 0000000..e6d8bd8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/LaguerreSolver.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.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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 <b>A First Course in Numerical Analysis</b>,
+ * ISBN 048641454X, chapter 8.
+ * <p>
+ * 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.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class LaguerreSolver extends UnivariateRealSolverImpl {
+
+ /** polynomial function to solve.
+ * @deprecated as of 2.0 the function is not stored anymore in the instance
+ */
+ @Deprecated
+ private final PolynomialFunction p;
+
+ /**
+ * Construct a solver for the given function.
+ *
+ * @param f function to solve
+ * @throws IllegalArgumentException if function is not polynomial
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public LaguerreSolver(UnivariateRealFunction f) throws IllegalArgumentException {
+ super(f, 100, 1E-6);
+ if (f instanceof PolynomialFunction) {
+ p = (PolynomialFunction) f;
+ } else {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_POLYNOMIAL);
+ }
+ }
+
+ /**
+ * Construct a solver.
+ * @deprecated in 2.2 (to be removed in 3.0)
+ */
+ @Deprecated
+ public LaguerreSolver() {
+ super(100, 1E-6);
+ p = null;
+ }
+
+ /**
+ * Returns a copy of the polynomial function.
+ *
+ * @return a fresh copy of the polynomial function
+ * @deprecated as of 2.0 the function is not stored anymore within the instance.
+ */
+ @Deprecated
+ public PolynomialFunction getPolynomialFunction() {
+ return new PolynomialFunction(p.getCoefficients());
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(p, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(p, min, max, initial);
+ }
+
+ /**
+ * Find a real root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f function to solve (must be polynomial)
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a real root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f function to solve (must be polynomial)
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+
+ // check for zeros before verifying bracketing
+ if (f.value(min) == 0.0) {
+ return min;
+ }
+ if (f.value(max) == 0.0) {
+ return max;
+ }
+ if (f.value(initial) == 0.0) {
+ return initial;
+ }
+
+ verifyBracketing(min, max, f);
+ verifySequence(min, initial, max);
+ if (isBracketing(min, initial, f)) {
+ return solve(f, min, initial);
+ } else {
+ return solve(f, initial, max);
+ }
+
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * Despite the bracketing condition, the root returned by solve(Complex[],
+ * Complex) may not be a real zero inside [min, max]. For example,
+ * p(x) = x^3 + 1, min = -2, max = 2, initial = 0. We can either try
+ * another initial value, or, as we did here, call solveAll() to obtain
+ * all roots and pick up the one that we're looking for.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * Despite the bracketing condition, the root returned by solve(Complex[],
+ * Complex) may not be a real zero inside [min, max]. For example,
+ * p(x) = x^3 + 1, min = -2, max = 2, initial = 0. We can either try
+ * another initial value, or, as we did here, call solveAll() to obtain
+ * all roots and pick up the one that we're looking for.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the point at which the function value is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+
+ // check function type
+ if (!(f instanceof PolynomialFunction)) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_POLYNOMIAL);
+ }
+
+ // check for zeros before verifying bracketing
+ if (f.value(min) == 0.0) { return min; }
+ if (f.value(max) == 0.0) { return max; }
+ verifyBracketing(min, max, f);
+
+ double coefficients[] = ((PolynomialFunction) f).getCoefficients();
+ Complex c[] = new Complex[coefficients.length];
+ for (int i = 0; i < coefficients.length; i++) {
+ c[i] = new Complex(coefficients[i], 0.0);
+ }
+ Complex initial = new Complex(0.5 * (min + max), 0.0);
+ Complex z = solve(c, initial);
+ if (isRootOK(min, max, z)) {
+ setResult(z.getReal(), iterationCount);
+ return result;
+ }
+
+ // solve all roots and select the one we're seeking
+ Complex[] root = solveAll(c, initial);
+ for (int i = 0; i < root.length; i++) {
+ if (isRootOK(min, max, root[i])) {
+ setResult(root[i].getReal(), iterationCount);
+ return result;
+ }
+ }
+
+ // should never happen
+ throw new ConvergenceException();
+ }
+
+ /**
+ * Returns true iff the given complex root is actually a real zero
+ * in the given interval, within the solver tolerance level.
+ *
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param z the complex root
+ * @return true iff z is the sought-after real zero
+ */
+ protected boolean isRootOK(double min, double max, Complex z) {
+ double tolerance = FastMath.max(relativeAccuracy * z.abs(), absoluteAccuracy);
+ return (isSequence(min, z.getReal(), max)) &&
+ (FastMath.abs(z.getImaginary()) <= tolerance ||
+ z.abs() <= functionValueAccuracy);
+ }
+
+ /**
+ * Find all complex roots for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ *
+ * @param coefficients the polynomial coefficients array
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2.
+ */
+ @Deprecated
+ public Complex[] solveAll(double coefficients[], double initial) throws
+ ConvergenceException, FunctionEvaluationException {
+
+ Complex c[] = new Complex[coefficients.length];
+ Complex z = new Complex(initial, 0.0);
+ for (int i = 0; i < c.length; i++) {
+ c[i] = new Complex(coefficients[i], 0.0);
+ }
+ return solveAll(c, z);
+ }
+
+ /**
+ * Find all complex roots for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ *
+ * @param coefficients the polynomial coefficients array
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2.
+ */
+ @Deprecated
+ public Complex[] solveAll(Complex coefficients[], Complex initial) throws
+ MaxIterationsExceededException, FunctionEvaluationException {
+
+ int n = coefficients.length - 1;
+ int iterationCount = 0;
+ if (n < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NON_POSITIVE_POLYNOMIAL_DEGREE, n);
+ }
+ Complex c[] = new Complex[n+1]; // coefficients for deflated polynomial
+ for (int i = 0; i <= n; i++) {
+ c[i] = coefficients[i];
+ }
+
+ // solve individual root successively
+ Complex root[] = new Complex[n];
+ for (int i = 0; i < n; i++) {
+ 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]));
+ }
+ iterationCount += this.iterationCount;
+ }
+
+ resultComputed = true;
+ this.iterationCount = iterationCount;
+ return root;
+ }
+
+ /**
+ * Find a complex root for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ *
+ * @param coefficients the polynomial coefficients array
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2.
+ */
+ @Deprecated
+ public Complex solve(Complex coefficients[], Complex initial) throws
+ MaxIterationsExceededException, FunctionEvaluationException {
+
+ int n = coefficients.length - 1;
+ if (n < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NON_POSITIVE_POLYNOMIAL_DEGREE, n);
+ }
+ Complex N = new Complex(n, 0.0);
+ Complex N1 = new Complex(n - 1, 0.0);
+
+ int i = 1;
+ Complex pv = null;
+ Complex dv = null;
+ Complex d2v = null;
+ Complex G = null;
+ Complex G2 = null;
+ Complex H = null;
+ Complex delta = null;
+ Complex denominator = null;
+ Complex z = initial;
+ Complex oldz = new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ while (i <= maximalIterationCount) {
+ // Compute pv (polynomial value), dv (derivative value), and
+ // d2v (second derivative value) simultaneously.
+ pv = coefficients[n];
+ dv = Complex.ZERO;
+ 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
+ double tolerance = FastMath.max(relativeAccuracy * z.abs(),
+ absoluteAccuracy);
+ if ((z.subtract(oldz)).abs() <= tolerance) {
+ resultComputed = true;
+ iterationCount = i;
+ return z;
+ }
+ if (pv.abs() <= functionValueAccuracy) {
+ resultComputed = true;
+ iterationCount = i;
+ return z;
+ }
+
+ // now pv != 0, calculate the new approximation
+ G = dv.divide(pv);
+ G2 = G.multiply(G);
+ H = G2.subtract(d2v.divide(pv));
+ delta = N1.multiply((N.multiply(H)).subtract(G2));
+ // choose a denominator larger in magnitude
+ Complex deltaSqrt = delta.sqrt();
+ Complex dplus = G.add(deltaSqrt);
+ Complex dminus = G.subtract(deltaSqrt);
+ 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(N.divide(denominator));
+ }
+ i++;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.java
new file mode 100644
index 0000000..a6d03bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/MullerSolver.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.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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. Methods solve() and solve2() find
+ * real zeros, using different ways to bypass complex arithmetics.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class MullerSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Construct a solver for the given function.
+ *
+ * @param f function to solve
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public MullerSolver(UnivariateRealFunction f) {
+ super(f, 100, 1E-6);
+ }
+
+ /**
+ * Construct a solver.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public MullerSolver() {
+ super(100, 1E-6);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a real root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a real root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ // check for zeros before verifying bracketing
+ if (f.value(min) == 0.0) { return min; }
+ if (f.value(max) == 0.0) { return max; }
+ if (f.value(initial) == 0.0) { return initial; }
+
+ verifyBracketing(min, max, f);
+ verifySequence(min, initial, max);
+ if (isBracketing(min, initial, f)) {
+ return solve(f, min, initial);
+ } else {
+ return solve(f, initial, max);
+ }
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * Original Muller's 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>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * Original Muller's 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>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ // [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 = f.value(x0);
+ double x2 = max;
+ double y2 = f.value(x2);
+ double x1 = 0.5 * (x0 + x2);
+ double y1 = f.value(x1);
+
+ // check for zeros before verifying bracketing
+ if (y0 == 0.0) {
+ return min;
+ }
+ if (y2 == 0.0) {
+ return max;
+ }
+ verifyBracketing(min, max, f);
+
+ double oldx = Double.POSITIVE_INFINITY;
+ for (int i = 1; i <= maximalIterationCount; ++i) {
+ // 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 = f.value(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance) {
+ setResult(x, i);
+ return result;
+ }
+ if (FastMath.abs(y) <= functionValueAccuracy) {
+ setResult(x, i);
+ return result;
+ }
+
+ // 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 = f.value(xm);
+ if (MathUtils.sign(y0) + MathUtils.sign(ym) == 0.0) {
+ x2 = xm; y2 = ym;
+ } else {
+ x0 = xm; y0 = ym;
+ }
+ x1 = 0.5 * (x0 + x2);
+ y1 = f.value(x1);
+ oldx = Double.POSITIVE_INFINITY;
+ }
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * solve2() differs from solve() in the way it avoids complex operations.
+ * Except for the initial [min, max], solve2() does not require bracketing
+ * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If complex
+ * number arises in the computation, we simply use its modulus as real
+ * approximation.</p>
+ * <p>
+ * Because the interval may not be bracketing, bisection alternative is
+ * not applicable here. However in practice our treatment usually works
+ * well, especially near real zeros where the imaginary part of complex
+ * approximation is often negligible.</p>
+ * <p>
+ * The formulas here do not use divided differences directly.</p>
+ *
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated replaced by {@link #solve2(UnivariateRealFunction, double, double)}
+ * since 2.0
+ */
+ @Deprecated
+ public double solve2(final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve2(f, min, max);
+ }
+
+ /**
+ * Find a real root in the given interval.
+ * <p>
+ * solve2() differs from solve() in the way it avoids complex operations.
+ * Except for the initial [min, max], solve2() does not require bracketing
+ * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If complex
+ * number arises in the computation, we simply use its modulus as real
+ * approximation.</p>
+ * <p>
+ * Because the interval may not be bracketing, bisection alternative is
+ * not applicable here. However in practice our treatment usually works
+ * well, especially near real zeros where the imaginary part of complex
+ * approximation is often negligible.</p>
+ * <p>
+ * The formulas here do not use divided differences directly.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve2(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ // 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 = f.value(x0);
+ double x1 = max;
+ double y1 = f.value(x1);
+ double x2 = 0.5 * (x0 + x1);
+ double y2 = f.value(x2);
+
+ // check for zeros before verifying bracketing
+ if (y0 == 0.0) { return min; }
+ if (y1 == 0.0) { return max; }
+ verifyBracketing(min, max, f);
+
+ double oldx = Double.POSITIVE_INFINITY;
+ for (int i = 1; i <= maximalIterationCount; ++i) {
+ // 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 = f.value(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance) {
+ setResult(x, i);
+ return result;
+ }
+ if (FastMath.abs(y) <= functionValueAccuracy) {
+ setResult(x, i);
+ return result;
+ }
+
+ // prepare the next iteration
+ x0 = x1;
+ y0 = y1;
+ x1 = x2;
+ y1 = y2;
+ x2 = x;
+ y2 = y;
+ oldx = x;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.java
new file mode 100644
index 0000000..a2cffff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/NewtonSolver.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.math.analysis.solvers;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class NewtonSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Construct a solver for the given function.
+ * @param f function to solve.
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public NewtonSolver(DifferentiableUnivariateRealFunction f) {
+ super(f, 100, 1E-6);
+ }
+
+ /**
+ * Construct a solver.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public NewtonSolver() {
+ super(100, 1E-6);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max, final double startValue)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max, startValue);
+ }
+
+ /**
+ * Find a zero near the midpoint of <code>min</code> and <code>max</code>.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative
+ * @throws IllegalArgumentException if min is not less than max
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a zero near the midpoint of <code>min</code> and <code>max</code>.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative
+ * @throws IllegalArgumentException if min is not less than max
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max, UnivariateRealSolverUtils.midpoint(min, max));
+ }
+
+ /**
+ * Find a zero near the value <code>startValue</code>.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval (ignored).
+ * @param max the upper bound for the interval (ignored).
+ * @param startValue the start value to use.
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative
+ * @throws IllegalArgumentException if startValue is not between min and max or
+ * if function is not a {@link DifferentiableUnivariateRealFunction} instance
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double startValue)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, startValue);
+ }
+
+ /**
+ * Find a zero near the value <code>startValue</code>.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval (ignored).
+ * @param max the upper bound for the interval (ignored).
+ * @param startValue the start value to use.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function or derivative
+ * @throws IllegalArgumentException if startValue is not between min and max or
+ * if function is not a {@link DifferentiableUnivariateRealFunction} instance
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double startValue)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ try {
+
+ final UnivariateRealFunction derivative =
+ ((DifferentiableUnivariateRealFunction) f).derivative();
+ clearResult();
+ verifySequence(min, startValue, max);
+
+ double x0 = startValue;
+ double x1;
+
+ int i = 0;
+ while (i < maximalIterationCount) {
+
+ x1 = x0 - (f.value(x0) / derivative.value(x0));
+ if (FastMath.abs(x1 - x0) <= absoluteAccuracy) {
+ setResult(x1, i);
+ return x1;
+ }
+
+ x0 = x1;
+ ++i;
+ }
+
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ } catch (ClassCastException cce) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.FUNCTION_NOT_DIFFERENTIABLE);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java
new file mode 100644
index 0000000..4f316c7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/RiddersSolver.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class RiddersSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Construct a solver for the given function.
+ *
+ * @param f function to solve
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public RiddersSolver(UnivariateRealFunction f) {
+ super(f, 100, 1E-6);
+ }
+
+ /**
+ * Construct a solver.
+ * @deprecated in 2.2
+ */
+ @Deprecated
+ public RiddersSolver() {
+ super(100, 1E-6);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a root in the given interval with initial value.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ // check for zeros before verifying bracketing
+ if (f.value(min) == 0.0) { return min; }
+ if (f.value(max) == 0.0) { return max; }
+ if (f.value(initial) == 0.0) { return initial; }
+
+ verifyBracketing(min, max, f);
+ verifySequence(min, initial, max);
+ if (isBracketing(min, initial, f)) {
+ return solve(f, min, initial);
+ } else {
+ return solve(f, initial, max);
+ }
+ }
+
+ /**
+ * Find a root in the given interval.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param maxEval Maximum number of evaluations.
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a root in the given interval.
+ * <p>
+ * Requires bracketing condition.</p>
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the point at which the function value is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if any parameters are invalid
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ // [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 = f.value(x1);
+ double x2 = max;
+ double y2 = f.value(x2);
+
+ // check for zeros before verifying bracketing
+ if (y1 == 0.0) {
+ return min;
+ }
+ if (y2 == 0.0) {
+ return max;
+ }
+ verifyBracketing(min, max, f);
+
+ int i = 1;
+ double oldx = Double.POSITIVE_INFINITY;
+ while (i <= maximalIterationCount) {
+ // calculate the new root approximation
+ final double x3 = 0.5 * (x1 + x2);
+ final double y3 = f.value(x3);
+ if (FastMath.abs(y3) <= functionValueAccuracy) {
+ setResult(x3, i);
+ return result;
+ }
+ final double delta = 1 - (y1 * y2) / (y3 * y3); // delta > 1 due to bracketing
+ final double correction = (MathUtils.sign(y2) * MathUtils.sign(y3)) *
+ (x3 - x1) / FastMath.sqrt(delta);
+ final double x = x3 - correction; // correction != 0
+ final double y = f.value(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance) {
+ setResult(x, i);
+ return result;
+ }
+ if (FastMath.abs(y) <= functionValueAccuracy) {
+ setResult(x, i);
+ return result;
+ }
+
+ // prepare the new interval for next iteration
+ // Ridders' method guarantees x1 < x < x2
+ if (correction > 0.0) { // x1 < x < x3
+ if (MathUtils.sign(y1) + MathUtils.sign(y) == 0.0) {
+ x2 = x;
+ y2 = y;
+ } else {
+ x1 = x;
+ x2 = x3;
+ y1 = y;
+ y2 = y3;
+ }
+ } else { // x3 < x < x2
+ if (MathUtils.sign(y2) + MathUtils.sign(y) == 0.0) {
+ x1 = x;
+ y1 = y;
+ } else {
+ x1 = x3;
+ x2 = x;
+ y1 = y3;
+ y2 = y;
+ }
+ }
+ oldx = x;
+ i++;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.java
new file mode 100644
index 0000000..325b662
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/SecantSolver.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.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * Implements a modified version of the
+ * <a href="http://mathworld.wolfram.com/SecantMethod.html">secant method</a>
+ * for approximating a zero of a real univariate function.
+ * <p>
+ * The algorithm is modified to maintain bracketing of a root by successive
+ * approximations. Because of forced bracketing, convergence may be slower than
+ * the unrestricted secant algorithm. However, this implementation should in
+ * general outperform the
+ * <a href="http://mathworld.wolfram.com/MethodofFalsePosition.html">
+ * regula falsi method.</a></p>
+ * <p>
+ * The function is assumed to be continuous but not necessarily smooth.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class SecantSolver extends UnivariateRealSolverImpl {
+
+ /**
+ * Construct a solver for the given function.
+ * @param f function to solve.
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ public SecantSolver(UnivariateRealFunction f) {
+ super(f, 100, 1E-6);
+ }
+
+ /**
+ * Construct a solver.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public SecantSolver() {
+ super(100, 1E-6);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double solve(final double min, final double max, final double initial)
+ throws ConvergenceException, FunctionEvaluationException {
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a zero in the given interval.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use (ignored)
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max, initial);
+ }
+
+ /**
+ * Find a zero in the given interval.
+ *
+ * @param f the function to solve
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param initial the start value to use (ignored)
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max, final double initial)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a zero in the given interval.
+ * @param f the function to solve
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ setMaximalIterationCount(maxEval);
+ return solve(f, min, max);
+ }
+
+ /**
+ * Find a zero in the given interval.
+ * @param f the function to solve
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @return the value where the function is zero
+ * @throws MaxIterationsExceededException if the maximum iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min is not less than max or the
+ * signs of the values of the function at the endpoints are not opposites
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public double solve(final UnivariateRealFunction f,
+ final double min, final double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+
+ clearResult();
+ verifyInterval(min, max);
+
+ // Index 0 is the old approximation for the root.
+ // Index 1 is the last calculated approximation for the root.
+ // Index 2 is a bracket for the root with respect to x0.
+ // OldDelta is the length of the bracketing interval of the last
+ // iteration.
+ double x0 = min;
+ double x1 = max;
+ double y0 = f.value(x0);
+ double y1 = f.value(x1);
+
+ // Verify bracketing
+ if (y0 * y1 >= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, min, max, y0, y1);
+ }
+
+ double x2 = x0;
+ double y2 = y0;
+ double oldDelta = x2 - x1;
+ int i = 0;
+ while (i < maximalIterationCount) {
+ if (FastMath.abs(y2) < FastMath.abs(y1)) {
+ x0 = x1;
+ x1 = x2;
+ x2 = x0;
+ y0 = y1;
+ y1 = y2;
+ y2 = y0;
+ }
+ if (FastMath.abs(y1) <= functionValueAccuracy) {
+ setResult(x1, i);
+ return result;
+ }
+ if (FastMath.abs(oldDelta) <
+ FastMath.max(relativeAccuracy * FastMath.abs(x1), absoluteAccuracy)) {
+ setResult(x1, i);
+ return result;
+ }
+ double delta;
+ if (FastMath.abs(y1) > FastMath.abs(y0)) {
+ // Function value increased in last iteration. Force bisection.
+ delta = 0.5 * oldDelta;
+ } else {
+ delta = (x0 - x1) / (1 - y0 / y1);
+ if (delta / oldDelta > 1) {
+ // New approximation falls outside bracket.
+ // Fall back to bisection.
+ delta = 0.5 * oldDelta;
+ }
+ }
+ x0 = x1;
+ y0 = y1;
+ x1 = x1 + delta;
+ y1 = f.value(x1);
+ if ((y1 > 0) == (y2 > 0)) {
+ // New bracket is (x0,x1).
+ x2 = x0;
+ y2 = y0;
+ }
+ oldDelta = x2 - x1;
+ i++;
+ }
+ throw new MaxIterationsExceededException(maximalIterationCount);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.java
new file mode 100644
index 0000000..6540f67
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.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.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.ConvergingAlgorithm;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+
+/**
+ * Interface for (univariate real) rootfinding algorithms.
+ * <p>
+ * Implementations will search for only one zero in the given interval.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public interface UnivariateRealSolver extends ConvergingAlgorithm {
+
+ /**
+ * Set the function value accuracy.
+ * <p>
+ * This is used to determine when an evaluated function value or some other
+ * value which is used as divisor is zero.</p>
+ * <p>
+ * This is a safety guard and it shouldn't be necessary to change this in
+ * general.</p>
+ *
+ * @param accuracy the accuracy.
+ * @throws IllegalArgumentException if the accuracy can't be achieved by
+ * the solver or is otherwise deemed unreasonable.
+ */
+ void setFunctionValueAccuracy(double accuracy);
+
+ /**
+ * Get the actual function value accuracy.
+ * @return the accuracy
+ */
+ double getFunctionValueAccuracy();
+
+ /**
+ * Reset the actual function accuracy to the default.
+ * The default value is provided by the solver implementation.
+ */
+ void resetFunctionValueAccuracy();
+
+ /**
+ * Solve for a zero root in the given interval.
+ * <p>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.</p>
+ *
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the solver
+ * @deprecated replaced by {@link #solve(UnivariateRealFunction, double, double)}
+ * since 2.0
+ */
+ @Deprecated
+ double solve(double min, double max) throws ConvergenceException, FunctionEvaluationException;
+
+ /**
+ * Solve for a zero root in the given interval.
+ * <p>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.</p>
+ *
+ * @param f the function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the solver
+ * @since 2.0
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ double solve(UnivariateRealFunction f, double min, double max)
+ throws ConvergenceException, FunctionEvaluationException;
+
+ /**
+ * Solve for a zero in the given interval, start at startValue.
+ * <p>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.</p>
+ *
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param startValue the start value to use
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the arguments do not
+ * satisfy the requirements specified by the solver
+ * @deprecated replaced by {@link #solve(UnivariateRealFunction, double, double, double)}
+ * since 2.0
+ */
+ @Deprecated
+ double solve(double min, double max, double startValue)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException;
+
+ /**
+ * Solve for a zero in the given interval, start at startValue.
+ * <p>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.</p>
+ *
+ * @param f the function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param startValue the start value to use
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the arguments do not
+ * satisfy the requirements specified by the solver
+ * @since 2.0
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ double solve(UnivariateRealFunction f, double min, double max, double startValue)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException;
+
+ /**
+ * Get the result of the last run of the solver.
+ *
+ * @return the last result.
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed.
+ */
+ double getResult();
+
+ /**
+ * Get the result of the last run of the solver.
+ *
+ * @return the value of the function at the last result.
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed.
+ */
+ double getFunctionValue();
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.java
new file mode 100644
index 0000000..46b7234
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.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.math.analysis.solvers;
+
+/**
+ * Abstract factory class used to create {@link UnivariateRealSolver} instances.
+ * <p>
+ * Solvers implementing the following algorithms are supported:
+ * <ul>
+ * <li>Bisection</li>
+ * <li>Brent's method</li>
+ * <li>Secant method</li>
+ * </ul>
+ * Concrete factories extending this class also specify a default solver, instances of which
+ * are returned by <code>newDefaultSolver()</code>.</p>
+ * <p>
+ * Common usage:<pre>
+ * SolverFactory factory = UnivariateRealSolverFactory.newInstance();</p>
+ *
+ * // create a Brent solver to use
+ * BrentSolver solver = factory.newBrentSolver();
+ * </pre>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public abstract class UnivariateRealSolverFactory {
+ /**
+ * Default constructor.
+ */
+ protected UnivariateRealSolverFactory() {
+ }
+
+ /**
+ * Create a new factory.
+ * @return a new factory.
+ */
+ public static UnivariateRealSolverFactory newInstance() {
+ return new UnivariateRealSolverFactoryImpl();
+ }
+
+ /**
+ * Create a new {@link UnivariateRealSolver}. The
+ * actual solver returned is determined by the underlying factory.
+ * @return the new solver.
+ */
+ public abstract UnivariateRealSolver newDefaultSolver();
+
+ /**
+ * Create a new {@link UnivariateRealSolver}. The
+ * solver is an implementation of the bisection method.
+ * @return the new solver.
+ */
+ public abstract UnivariateRealSolver newBisectionSolver();
+
+ /**
+ * Create a new {@link UnivariateRealSolver}. The
+ * solver is an implementation of the Brent method.
+ * @return the new solver.
+ */
+ public abstract UnivariateRealSolver newBrentSolver();
+
+ /**
+ * Create a new {@link UnivariateRealSolver}. The
+ * solver is an implementation of Newton's Method.
+ * @return the new solver.
+ */
+ public abstract UnivariateRealSolver newNewtonSolver();
+
+ /**
+ * Create a new {@link UnivariateRealSolver}. The
+ * solver is an implementation of the secant method.
+ * @return the new solver.
+ */
+ public abstract UnivariateRealSolver newSecantSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.java
new file mode 100644
index 0000000..cb4c6b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImpl.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.math.analysis.solvers;
+
+/**
+ * A concrete {@link UnivariateRealSolverFactory}. This is the default solver factory
+ * used by commons-math.
+ * <p>
+ * The default solver returned by this factory is a {@link BrentSolver}.</p>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class UnivariateRealSolverFactoryImpl extends UnivariateRealSolverFactory {
+
+ /**
+ * Default constructor.
+ */
+ public UnivariateRealSolverFactoryImpl() {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UnivariateRealSolver newDefaultSolver() {
+ return newBrentSolver();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UnivariateRealSolver newBisectionSolver() {
+ return new BisectionSolver();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UnivariateRealSolver newBrentSolver() {
+ return new BrentSolver();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UnivariateRealSolver newNewtonSolver() {
+ return new NewtonSolver();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UnivariateRealSolver newSecantSolver() {
+ return new SecantSolver();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java
new file mode 100644
index 0000000..557c767
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverImpl.java
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergingAlgorithmImpl;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * solvers.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+@Deprecated
+public abstract class UnivariateRealSolverImpl
+ extends ConvergingAlgorithmImpl implements UnivariateRealSolver {
+
+ /** Maximum error of function. */
+ protected double functionValueAccuracy;
+
+ /** Default maximum error of function. */
+ protected double defaultFunctionValueAccuracy;
+
+ /** Indicates where a root has been computed. */
+ protected boolean resultComputed = false;
+
+ /** The last computed root. */
+ protected double result;
+
+ /** Value of the function at the last computed result. */
+ protected double functionValue;
+
+ /** The function to solve.
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method. */
+ @Deprecated
+ protected UnivariateRealFunction f;
+
+ /**
+ * Construct a solver with given iteration count and accuracy.
+ *
+ * @param f the function to solve.
+ * @param defaultAbsoluteAccuracy maximum absolute error
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the
+ * defaultAbsoluteAccuracy is not valid
+ * @deprecated as of 2.0 the function to solve is passed as an argument
+ * to the {@link #solve(UnivariateRealFunction, double, double)} or
+ * {@link UnivariateRealSolverImpl#solve(UnivariateRealFunction, double, double, double)}
+ * method.
+ */
+ @Deprecated
+ protected UnivariateRealSolverImpl(final UnivariateRealFunction f,
+ final int defaultMaximalIterationCount,
+ final double defaultAbsoluteAccuracy) {
+ super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
+ if (f == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ this.f = f;
+ this.defaultFunctionValueAccuracy = 1.0e-15;
+ this.functionValueAccuracy = defaultFunctionValueAccuracy;
+ }
+
+ /**
+ * Construct a solver with given iteration count and accuracy.
+ *
+ * @param defaultAbsoluteAccuracy maximum absolute error
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the
+ * defaultAbsoluteAccuracy is not valid
+ */
+ protected UnivariateRealSolverImpl(final int defaultMaximalIterationCount,
+ final double defaultAbsoluteAccuracy) {
+ super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
+ this.defaultFunctionValueAccuracy = 1.0e-15;
+ this.functionValueAccuracy = defaultFunctionValueAccuracy;
+ }
+
+ /** Check if a result has been computed.
+ * @exception IllegalStateException if no result has been computed
+ */
+ protected void checkResultComputed() throws IllegalStateException {
+ if (!resultComputed) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_RESULT_AVAILABLE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double getResult() {
+ checkResultComputed();
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public double getFunctionValue() {
+ checkResultComputed();
+ return functionValue;
+ }
+
+ /** {@inheritDoc} */
+ public void setFunctionValueAccuracy(final double accuracy) {
+ functionValueAccuracy = accuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double getFunctionValueAccuracy() {
+ return functionValueAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public void resetFunctionValueAccuracy() {
+ functionValueAccuracy = defaultFunctionValueAccuracy;
+ }
+
+ /**
+ * Solve for a zero root in the given interval.
+ * <p>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.</p>
+ *
+ * @param function the function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param maxEval Maximum number of evaluations.
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the solver
+ * @since 2.2
+ */
+ public double solve(int maxEval, UnivariateRealFunction function, double min, double max)
+ throws ConvergenceException, FunctionEvaluationException {
+ throw MathRuntimeException.createUnsupportedOperationException(LocalizedFormats.NOT_OVERRIDEN);
+ }
+
+ /**
+ * Solve for a zero in the given interval, start at startValue.
+ * <p>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.</p>
+ *
+ * @param function the function to solve.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param startValue the start value to use
+ * @param maxEval Maximum number of evaluations.
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the solver detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if min > max or the arguments do not
+ * satisfy the requirements specified by the solver
+ * @since 2.2
+ */
+ public double solve(int maxEval, UnivariateRealFunction function, double min, double max, double startValue)
+ throws ConvergenceException, FunctionEvaluationException, IllegalArgumentException {
+ throw MathRuntimeException.createUnsupportedOperationException(LocalizedFormats.NOT_OVERRIDEN);
+ }
+
+ /**
+ * Convenience function for implementations.
+ *
+ * @param newResult the result to set
+ * @param iterationCount the iteration count to set
+ */
+ protected final void setResult(final double newResult, final int iterationCount) {
+ this.result = newResult;
+ this.iterationCount = iterationCount;
+ this.resultComputed = true;
+ }
+
+ /**
+ * Convenience function for implementations.
+ *
+ * @param x the result to set
+ * @param fx the result to set
+ * @param iterationCount the iteration count to set
+ */
+ protected final void setResult(final double x, final double fx,
+ final int iterationCount) {
+ this.result = x;
+ this.functionValue = fx;
+ this.iterationCount = iterationCount;
+ this.resultComputed = true;
+ }
+
+ /**
+ * Convenience function for implementations.
+ */
+ protected final void clearResult() {
+ this.iterationCount = 0;
+ this.resultComputed = false;
+ }
+
+ /**
+ * Returns true iff the function takes opposite signs at the endpoints.
+ *
+ * @param lower the lower endpoint
+ * @param upper the upper endpoint
+ * @param function the function
+ * @return true if f(lower) * f(upper) < 0
+ * @throws FunctionEvaluationException if an error occurs evaluating the function at the endpoints
+ */
+ protected boolean isBracketing(final double lower, final double upper,
+ final UnivariateRealFunction function)
+ throws FunctionEvaluationException {
+ final double f1 = function.value(lower);
+ final double f2 = function.value(upper);
+ return (f1 > 0 && f2 < 0) || (f1 < 0 && f2 > 0);
+ }
+
+ /**
+ * Returns true if the arguments form a (strictly) increasing sequence
+ *
+ * @param start first number
+ * @param mid second number
+ * @param end third number
+ * @return true if the arguments form an increasing sequence
+ */
+ protected boolean isSequence(final double start, final double mid, final double end) {
+ return (start < mid) && (mid < end);
+ }
+
+ /**
+ * Verifies that the endpoints specify an interval,
+ * throws IllegalArgumentException if not
+ *
+ * @param lower lower endpoint
+ * @param upper upper endpoint
+ * @throws IllegalArgumentException
+ */
+ protected void verifyInterval(final double lower, final double upper) {
+ if (lower >= upper) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper);
+ }
+ }
+
+ /**
+ * Verifies that <code>lower < initial < upper</code>
+ * throws IllegalArgumentException if not
+ *
+ * @param lower lower endpoint
+ * @param initial initial value
+ * @param upper upper endpoint
+ * @throws IllegalArgumentException
+ */
+ protected void verifySequence(final double lower, final double initial, final double upper) {
+ if (!isSequence(lower, initial, upper)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS,
+ lower, initial, upper);
+ }
+ }
+
+ /**
+ * Verifies that the endpoints specify an interval and the function takes
+ * opposite signs at the endpoints, throws IllegalArgumentException if not
+ *
+ * @param lower lower endpoint
+ * @param upper upper endpoint
+ * @param function function
+ * @throws IllegalArgumentException
+ * @throws FunctionEvaluationException if an error occurs evaluating the function at the endpoints
+ */
+ protected void verifyBracketing(final double lower, final double upper,
+ final UnivariateRealFunction function)
+ throws FunctionEvaluationException {
+
+ verifyInterval(lower, upper);
+ if (!isBracketing(lower, upper, function)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SAME_SIGN_AT_ENDPOINTS,
+ lower, upper, function.value(lower), function.value(upper));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.java
new file mode 100644
index 0000000..3186d6a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtils.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.math.analysis.solvers;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Utility routines for {@link UnivariateRealSolver} objects.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class UnivariateRealSolverUtils {
+
+ /**
+ * Default constructor.
+ */
+ private UnivariateRealSolverUtils() {
+ super();
+ }
+
+ /**
+ * Convenience method to find a zero of a univariate real function. A default
+ * solver is used.
+ *
+ * @param f the function.
+ * @param x0 the lower bound for the interval.
+ * @param x1 the upper bound for the interval.
+ * @return a value where the function is zero.
+ * @throws ConvergenceException if the iteration count was exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if f is null or the endpoints do not
+ * specify a valid interval
+ */
+ public static double solve(UnivariateRealFunction f, double x0, double x1)
+ throws ConvergenceException, FunctionEvaluationException {
+ setup(f);
+ return LazyHolder.FACTORY.newDefaultSolver().solve(f, x0, x1);
+ }
+
+ /**
+ * Convenience method to find a zero of a univariate real function. A default
+ * solver is used.
+ *
+ * @param f the function
+ * @param x0 the lower bound for the interval
+ * @param x1 the upper bound for the interval
+ * @param absoluteAccuracy the accuracy to be used by the solver
+ * @return a value where the function is zero
+ * @throws ConvergenceException if the iteration count is exceeded
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if f is null, the endpoints do not
+ * specify a valid interval, or the absoluteAccuracy is not valid for the
+ * default solver
+ */
+ public static double solve(UnivariateRealFunction f, double x0, double x1,
+ double absoluteAccuracy) throws ConvergenceException,
+ FunctionEvaluationException {
+
+ setup(f);
+ UnivariateRealSolver solver = LazyHolder.FACTORY.newDefaultSolver();
+ solver.setAbsoluteAccuracy(absoluteAccuracy);
+ return solver.solve(f, x0, x1);
+ }
+
+ /**
+ * This method attempts to find two values a and b satisfying <ul>
+ * <li> <code> lowerBound <= a < initial < b <= upperBound</code> </li>
+ * <li> <code> f(a) * f(b) < 0 </code></li>
+ * </ul>
+ * If f is continuous on <code>[a,b],</code> this means that <code>a</code>
+ * and <code>b</code> bracket a root of f.
+ * <p>
+ * The algorithm starts by setting
+ * <code>a := initial -1; b := initial +1,</code> examines the value of the
+ * function at <code>a</code> and <code>b</code> and keeps moving
+ * the endpoints out by one unit each time through a loop that terminates
+ * when one of the following happens: <ul>
+ * <li> <code> f(a) * f(b) < 0 </code> -- success!</li>
+ * <li> <code> a = lower </code> and <code> b = upper</code>
+ * -- ConvergenceException </li>
+ * <li> <code> Integer.MAX_VALUE</code> iterations elapse
+ * -- ConvergenceException </li>
+ * </ul></p>
+ * <p>
+ * <strong>Note: </strong> this method can take
+ * <code>Integer.MAX_VALUE</code> iterations to throw a
+ * <code>ConvergenceException.</code> Unless you are confident that there
+ * is a root between <code>lowerBound</code> and <code>upperBound</code>
+ * near <code>initial,</code> it is better to use
+ * {@link #bracket(UnivariateRealFunction, double, double, double, int)},
+ * explicitly specifying the maximum number of iterations.</p>
+ *
+ * @param function the 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, b}
+ * @throws ConvergenceException if a root can not be bracketted
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if function is null, maximumIterations
+ * is not positive, or initial is not between lowerBound and upperBound
+ */
+ public static double[] bracket(UnivariateRealFunction function,
+ double initial, double lowerBound, double upperBound)
+ throws ConvergenceException, FunctionEvaluationException {
+ return bracket( function, initial, lowerBound, upperBound,
+ Integer.MAX_VALUE ) ;
+ }
+
+ /**
+ * This method attempts to find two values a and b satisfying <ul>
+ * <li> <code> lowerBound <= a < initial < b <= upperBound</code> </li>
+ * <li> <code> f(a) * f(b) <= 0 </code> </li>
+ * </ul>
+ * If f is continuous on <code>[a,b],</code> this means that <code>a</code>
+ * and <code>b</code> bracket a root of f.
+ * <p>
+ * The algorithm starts by setting
+ * <code>a := initial -1; b := initial +1,</code> examines the value of the
+ * function at <code>a</code> and <code>b</code> and keeps moving
+ * the endpoints out by one unit each time through a loop that terminates
+ * when one of the following happens: <ul>
+ * <li> <code> f(a) * f(b) <= 0 </code> -- success!</li>
+ * <li> <code> a = lower </code> and <code> b = upper</code>
+ * -- ConvergenceException </li>
+ * <li> <code> maximumIterations</code> iterations elapse
+ * -- ConvergenceException </li></ul></p>
+ *
+ * @param function the 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, b}.
+ * @throws ConvergenceException if the algorithm fails to find a and b
+ * satisfying the desired conditions
+ * @throws FunctionEvaluationException if an error occurs evaluating the function
+ * @throws IllegalArgumentException if function is null, maximumIterations
+ * is not positive, or initial is not between lowerBound and upperBound
+ */
+ public static double[] bracket(UnivariateRealFunction function,
+ double initial, double lowerBound, double upperBound,
+ int maximumIterations) throws ConvergenceException,
+ FunctionEvaluationException {
+
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ if (maximumIterations <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_MAX_ITERATIONS, maximumIterations);
+ }
+ if (initial < lowerBound || initial > upperBound || lowerBound >= upperBound) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_BRACKETING_PARAMETERS,
+ lowerBound, initial, upperBound);
+ }
+ double a = initial;
+ double b = initial;
+ double fa;
+ double fb;
+ int numIterations = 0 ;
+
+ do {
+ a = FastMath.max(a - 1.0, lowerBound);
+ b = FastMath.min(b + 1.0, upperBound);
+ fa = function.value(a);
+
+ fb = function.value(b);
+ numIterations++ ;
+ } while ((fa * fb > 0.0) && (numIterations < maximumIterations) &&
+ ((a > lowerBound) || (b < upperBound)));
+
+ if (fa * fb > 0.0 ) {
+ throw new ConvergenceException(
+ LocalizedFormats.FAILED_BRACKETING,
+ numIterations, maximumIterations, initial,
+ lowerBound, upperBound, a, b, fa, fb);
+ }
+
+ return new double[]{a, b};
+ }
+
+ /**
+ * 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) * .5;
+ }
+
+ /**
+ * Checks to see if f is null, throwing IllegalArgumentException if so.
+ * @param f input function
+ * @throws IllegalArgumentException if f is null
+ */
+ private static void setup(UnivariateRealFunction f) {
+ if (f == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the factory.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached solver factory */
+ private static final UnivariateRealSolverFactory FACTORY = UnivariateRealSolverFactory.newInstance();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+}
diff --git a/src/main/java/org/apache/commons/math/analysis/solvers/package.html b/src/main/java/org/apache/commons/math/analysis/solvers/package.html
new file mode 100644
index 0000000..bbb49d1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/analysis/solvers/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Root finding algorithms, for univariate real functions.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/complex/Complex.java b/src/main/java/org/apache/commons/math/complex/Complex.java
new file mode 100644
index 0000000..ad2bc96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/complex/Complex.java
@@ -0,0 +1,1007 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.complex;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Representation of a Complex number - a number which has both a
+ * real and imaginary part.
+ * <p>
+ * Implementations of arithmetic operations handle <code>NaN</code> and
+ * infinite values according to the rules for {@link java.lang.Double}
+ * arithmetic, applying definitional formulas and returning <code>NaN</code> or
+ * infinite values in real or imaginary parts as these arise in computation.
+ * See individual method javadocs for details.</p>
+ * <p>
+ * {@link #equals} identifies all values with <code>NaN</code> in either real
+ * or imaginary part - e.g., <pre>
+ * <code>1 + NaNi == NaN + i == NaN + NaNi.</code></pre></p>
+ *
+ * implements Serializable since 2.0
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+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 the real and imaginary parts.
+ *
+ * @param real the real part
+ * @param imaginary the imaginary part
+ */
+ public Complex(double real, double imaginary) {
+ super();
+ 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.
+ * <p>
+ * Returns <code>NaN</code> if either real or imaginary part is
+ * <code>NaN</code> and <code>Double.POSITIVE_INFINITY</code> if
+ * neither part is <code>NaN</code>, but at least one part takes an infinite
+ * value.</p>
+ *
+ * @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);
+ }
+ }
+
+ /**
+ * Return the sum of this complex number and the given complex number.
+ * <p>
+ * Uses the definitional formula
+ * <pre>
+ * (a + bi) + (c + di) = (a+c) + (b+d)i
+ * </pre></p>
+ * <p>
+ * If either this or <code>rhs</code> has a NaN value in either part,
+ * {@link #NaN} is returned; otherwise Inifinite and NaN values are
+ * returned in the parts of the result according to the rules for
+ * {@link java.lang.Double} arithmetic.</p>
+ *
+ * @param rhs the other complex number
+ * @return the complex number sum
+ * @throws NullPointerException if <code>rhs</code> is null
+ */
+ public Complex add(Complex rhs) {
+ return createComplex(real + rhs.getReal(),
+ imaginary + rhs.getImaginary());
+ }
+
+ /**
+ * Return the conjugate of this complex number. The conjugate of
+ * "A + Bi" is "A - Bi".
+ * <p>
+ * {@link #NaN} is returned if either the real or imaginary
+ * part of this Complex number equals <code>Double.NaN</code>.</p>
+ * <p>
+ * If the imaginary part is infinite, and the real part is not NaN,
+ * the returned value has infinite imaginary part of the opposite
+ * sign - e.g. the conjugate of <code>1 + POSITIVE_INFINITY i</code>
+ * is <code>1 - NEGATIVE_INFINITY i</code></p>
+ *
+ * @return the conjugate of this Complex object
+ */
+ public Complex conjugate() {
+ if (isNaN()) {
+ return NaN;
+ }
+ return createComplex(real, -imaginary);
+ }
+
+ /**
+ * Return the quotient of this complex number and the given complex number.
+ * <p>
+ * 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>
+ * <p>
+ * Infinite and NaN values are handled / returned according to the
+ * following rules, applied in the order presented:
+ * <ul>
+ * <li>If either this or <code>rhs</code> has a NaN value in either part,
+ * {@link #NaN} is returned.</li>
+ * <li>If <code>rhs</code> equals {@link #ZERO}, {@link #NaN} is returned.
+ * </li>
+ * <li>If this and <code>rhs</code> are both infinite,
+ * {@link #NaN} is returned.</li>
+ * <li>If this is finite (i.e., has no infinite or NaN parts) and
+ * <code>rhs</code> is infinite (one or both parts infinite),
+ * {@link #ZERO} is returned.</li>
+ * <li>If this is infinite and <code>rhs</code> is finite, NaN values are
+ * returned in the parts of the result if the {@link java.lang.Double}
+ * rules applied to the definitional formula force NaN results.</li>
+ * </ul></p>
+ *
+ * @param rhs the other complex number
+ * @return the complex number quotient
+ * @throws NullPointerException if <code>rhs</code> is null
+ */
+ public Complex divide(Complex rhs) {
+ if (isNaN() || rhs.isNaN()) {
+ return NaN;
+ }
+
+ double c = rhs.getReal();
+ double d = rhs.getImaginary();
+ if (c == 0.0 && d == 0.0) {
+ return NaN;
+ }
+
+ if (rhs.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);
+ }
+ }
+
+ /**
+ * Test for the equality of two Complex objects.
+ * <p>
+ * If both the real and imaginary parts of two Complex numbers
+ * are exactly the same, and neither is <code>Double.NaN</code>, the two
+ * Complex objects are considered to be equal.</p>
+ * <p>
+ * All <code>NaN</code> 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</code>, the complex number is equal to
+ * <code>Complex.NaN</code>.</p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two Complex objects are equal, false if
+ * object is null, not an instance of Complex, or
+ * not equal to this Complex instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Complex){
+ Complex rhs = (Complex)other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ } else {
+ return (real == rhs.real) && (imaginary == rhs.imaginary);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the complex number.
+ * <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 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;
+ }
+
+ /**
+ * Returns true if either or both parts of this complex number is NaN;
+ * false otherwise
+ *
+ * @return true if either or both parts of this complex number is NaN;
+ * false otherwise
+ */
+ public boolean isNaN() {
+ return isNaN;
+ }
+
+ /**
+ * Returns true if either the real or imaginary part of this complex number
+ * takes an infinite value (either <code>Double.POSITIVE_INFINITY</code> or
+ * <code>Double.NEGATIVE_INFINITY</code>) and neither part
+ * is <code>NaN</code>.
+ *
+ * @return true if one or both parts of this complex number are infinite
+ * and neither part is <code>NaN</code>
+ */
+ public boolean isInfinite() {
+ return isInfinite;
+ }
+
+ /**
+ * Return the product of this complex number and the given complex number.
+ * <p>
+ * Implements preliminary checks for NaN and infinity followed by
+ * the definitional formula:
+ * <pre><code>
+ * (a + bi)(c + di) = (ac - bd) + (ad + bc)i
+ * </code></pre>
+ * </p>
+ * <p>
+ * Returns {@link #NaN} if either this or <code>rhs</code> has one or more
+ * NaN parts.
+ * </p>
+ * Returns {@link #INF} if neither this nor <code>rhs</code> has one or more
+ * NaN parts and if either this or <code>rhs</code> has one or more
+ * infinite parts (same result is returned regardless of the sign of the
+ * components).
+ * </p>
+ * <p>
+ * Returns finite values in components of the result per the
+ * definitional formula in all remaining cases.
+ * </p>
+ *
+ * @param rhs the other complex number
+ * @return the complex number product
+ * @throws NullPointerException if <code>rhs</code> is null
+ */
+ public Complex multiply(Complex rhs) {
+ if (isNaN() || rhs.isNaN()) {
+ return NaN;
+ }
+ if (Double.isInfinite(real) || Double.isInfinite(imaginary) ||
+ Double.isInfinite(rhs.real)|| Double.isInfinite(rhs.imaginary)) {
+ // we don't use Complex.isInfinite() to avoid testing for NaN again
+ return INF;
+ }
+ return createComplex(real * rhs.real - imaginary * rhs.imaginary,
+ real * rhs.imaginary + imaginary * rhs.real);
+ }
+
+ /**
+ * Return the product of this complex number and the given scalar number.
+ * <p>
+ * Implements preliminary checks for NaN and infinity followed by
+ * the definitional formula:
+ * <pre><code>
+ * c(a + bi) = (ca) + (cb)i
+ * </code></pre>
+ * </p>
+ * <p>
+ * Returns {@link #NaN} if either this or <code>rhs</code> has one or more
+ * NaN parts.
+ * </p>
+ * Returns {@link #INF} if neither this nor <code>rhs</code> has one or more
+ * NaN parts and if either this or <code>rhs</code> has one or more
+ * infinite parts (same result is returned regardless of the sign of the
+ * components).
+ * </p>
+ * <p>
+ * Returns finite values in components of the result per the
+ * definitional formula in all remaining cases.
+ * </p>
+ *
+ * @param rhs the scalar number
+ * @return the complex number product
+ */
+ public Complex multiply(double rhs) {
+ if (isNaN() || Double.isNaN(rhs)) {
+ return NaN;
+ }
+ if (Double.isInfinite(real) || Double.isInfinite(imaginary) ||
+ Double.isInfinite(rhs)) {
+ // we don't use Complex.isInfinite() to avoid testing for NaN again
+ return INF;
+ }
+ return createComplex(real * rhs, imaginary * rhs);
+ }
+
+ /**
+ * Return the additive inverse of this complex number.
+ * <p>
+ * Returns <code>Complex.NaN</code> if either real or imaginary
+ * part of this Complex number equals <code>Double.NaN</code>.</p>
+ *
+ * @return the negation of this complex number
+ */
+ public Complex negate() {
+ if (isNaN()) {
+ return NaN;
+ }
+
+ return createComplex(-real, -imaginary);
+ }
+
+ /**
+ * Return the difference between this complex number and the given complex
+ * number.
+ * <p>
+ * Uses the definitional formula
+ * <pre>
+ * (a + bi) - (c + di) = (a-c) + (b-d)i
+ * </pre></p>
+ * <p>
+ * If either this or <code>rhs</code> has a NaN value in either part,
+ * {@link #NaN} is returned; otherwise inifinite and NaN values are
+ * returned in the parts of the result according to the rules for
+ * {@link java.lang.Double} arithmetic. </p>
+ *
+ * @param rhs the other complex number
+ * @return the complex number difference
+ * @throws NullPointerException if <code>rhs</code> is null
+ */
+ public Complex subtract(Complex rhs) {
+ if (isNaN() || rhs.isNaN()) {
+ return NaN;
+ }
+
+ return createComplex(real - rhs.getReal(),
+ imaginary - rhs.getImaginary());
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/InverseCosine.html" TARGET="_top">
+ * inverse cosine</a> of this complex number.
+ * <p>
+ * Implements the formula: <pre>
+ * <code> acos(z) = -i (log(z + i (sqrt(1 - z<sup>2</sup>))))</code></pre></p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code> or infinite.</p>
+ *
+ * @return the inverse cosine of this complex number
+ * @since 1.2
+ */
+ public Complex acos() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return this.add(this.sqrt1z().multiply(Complex.I)).log()
+ .multiply(Complex.I.negate());
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/InverseSine.html" TARGET="_top">
+ * inverse sine</a> of this complex number.
+ * <p>
+ * Implements the formula: <pre>
+ * <code> asin(z) = -i (log(sqrt(1 - z<sup>2</sup>) + iz)) </code></pre></p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code> or infinite.</p>
+ *
+ * @return the inverse sine of this complex number.
+ * @since 1.2
+ */
+ public Complex asin() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return sqrt1z().add(this.multiply(Complex.I)).log()
+ .multiply(Complex.I.negate());
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top">
+ * inverse tangent</a> of this complex number.
+ * <p>
+ * Implements the formula: <pre>
+ * <code> atan(z) = (i/2) log((i + z)/(i - z)) </code></pre></p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code> or infinite.</p>
+ *
+ * @return the inverse tangent of this complex number
+ * @since 1.2
+ */
+ public Complex atan() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return this.add(Complex.I).divide(Complex.I.subtract(this)).log()
+ .multiply(Complex.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.
+ * <p>
+ * Implements the formula: <pre>
+ * <code> cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i</code></pre>
+ * where the (real) functions on the right-hand side are
+ * {@link java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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 &#x2213; INFINITY i
+ * cos(&plusmn;INFINITY + i) = NaN + NaN i
+ * cos(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i</code></pre></p>
+ *
+ * @return the cosine of this complex number
+ * @since 1.2
+ */
+ public Complex cos() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return createComplex(FastMath.cos(real) * MathUtils.cosh(imaginary),
+ -FastMath.sin(real) * MathUtils.sinh(imaginary));
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
+ * hyperbolic cosine</a> of this complex number.
+ * <p>
+ * 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 java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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>
+ * 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></p>
+ *
+ * @return the hyperbolic cosine of this complex number.
+ * @since 1.2
+ */
+ public Complex cosh() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return createComplex(MathUtils.cosh(real) * FastMath.cos(imaginary),
+ MathUtils.sinh(real) * FastMath.sin(imaginary));
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/ExponentialFunction.html" TARGET="_top">
+ * exponential function</a> of this complex number.
+ * <p>
+ * 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 java.lang.Math#exp}, {@link java.lang.Math#cos}, and
+ * {@link java.lang.Math#sin}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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>
+ * 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></p>
+ *
+ * @return <i>e</i><sup><code>this</code></sup>
+ * @since 1.2
+ */
+ public Complex exp() {
+ if (isNaN()) {
+ return Complex.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.
+ * <p>
+ * 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 java.lang.Math#log},
+ * <code>|a + bi|</code> is the modulus, {@link Complex#abs}, and
+ * <code>arg(a + bi) = {@link java.lang.Math#atan2}(b, a)</code></p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <p>
+ * 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></p>
+ *
+ * @return ln of this complex number.
+ * @since 1.2
+ */
+ public Complex log() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return createComplex(FastMath.log(abs()),
+ FastMath.atan2(imaginary, real));
+ }
+
+ /**
+ * Returns of value of this complex number raised to the power of <code>x</code>.
+ * <p>
+ * Implements the formula: <pre>
+ * <code> y<sup>x</sup> = exp(x&middot;log(y))</code></pre>
+ * where <code>exp</code> and <code>log</code> are {@link #exp} and
+ * {@link #log}, respectively.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code> or infinite, or if <code>y</code>
+ * equals {@link Complex#ZERO}.</p>
+ *
+ * @param x the exponent.
+ * @return <code>this</code><sup><code>x</code></sup>
+ * @throws NullPointerException if x is null
+ * @since 1.2
+ */
+ public Complex pow(Complex x) {
+ if (x == null) {
+ throw new NullPointerException();
+ }
+ return this.log().multiply(x).exp();
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/Sine.html" TARGET="_top">
+ * sine</a>
+ * of this complex number.
+ * <p>
+ * 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 java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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>
+ * 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></p>
+ *
+ * @return the sine of this complex number.
+ * @since 1.2
+ */
+ public Complex sin() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return createComplex(FastMath.sin(real) * MathUtils.cosh(imaginary),
+ FastMath.cos(real) * MathUtils.sinh(imaginary));
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/HyperbolicSine.html" TARGET="_top">
+ * hyperbolic sine</a> of this complex number.
+ * <p>
+ * 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 java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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></p>
+ *
+ * @return the hyperbolic sine of this complex number
+ * @since 1.2
+ */
+ public Complex sinh() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ return createComplex(MathUtils.sinh(real) * FastMath.cos(imaginary),
+ MathUtils.cosh(real) * FastMath.sin(imaginary));
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
+ * square root</a> of this complex number.
+ * <p>
+ * Implements the following algorithm to compute <code>sqrt(a + bi)</code>:
+ * <ol><li>Let <code>t = sqrt((|a| + |a + bi|) / 2)</code></li>
+ * <li><pre>if <code> a &#8805; 0</code> return <code>t + (b/2t)i</code>
+ * else return <code>|b|/2t + sign(b)t i </code></pre></li>
+ * </ol>
+ * where <ul>
+ * <li><code>|a| = {@link Math#abs}(a)</code></li>
+ * <li><code>|a + bi| = {@link Complex#abs}(a + bi) </code></li>
+ * <li><code>sign(b) = {@link MathUtils#indicator}(b) </code>
+ * </ul></p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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>
+ * 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></p>
+ *
+ * @return the square root of this complex number
+ * @since 1.2
+ */
+ public Complex sqrt() {
+ if (isNaN()) {
+ return Complex.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),
+ MathUtils.indicator(imaginary) * t);
+ }
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
+ * square root</a> of 1 - <code>this</code><sup>2</sup> for this complex
+ * number.
+ * <p>
+ * Computes the result directly as
+ * <code>sqrt(Complex.ONE.subtract(z.multiply(z)))</code>.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <p>
+ * Infinite values in real or imaginary parts of the input may result in
+ * infinite or NaN values returned in parts of the result.</p>
+ *
+ * @return the square root of 1 - <code>this</code><sup>2</sup>
+ * @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.
+ * <p>
+ * 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 java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <p>
+ * 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(1 &plusmn; INFINITY i) = 0 + NaN i
+ * tan(&plusmn;INFINITY + i) = NaN + NaN i
+ * tan(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * tan(&plusmn;&pi;/2 + 0 i) = &plusmn;INFINITY + NaN i</code></pre></p>
+ *
+ * @return the tangent of this complex number
+ * @since 1.2
+ */
+ public Complex tan() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ double real2 = 2.0 * real;
+ double imaginary2 = 2.0 * imaginary;
+ double d = FastMath.cos(real2) + MathUtils.cosh(imaginary2);
+
+ return createComplex(FastMath.sin(real2) / d, MathUtils.sinh(imaginary2) / d);
+ }
+
+ /**
+ * Compute the
+ * <a href="http://mathworld.wolfram.com/HyperbolicTangent.html" TARGET="_top">
+ * hyperbolic tangent</a> of this complex number.
+ * <p>
+ * 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 java.lang.Math#sin}, {@link java.lang.Math#cos},
+ * {@link MathUtils#cosh} and {@link MathUtils#sinh}.</p>
+ * <p>
+ * Returns {@link Complex#NaN} if either real or imaginary part of the
+ * input argument is <code>NaN</code>.</p>
+ * <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>
+ * tanh(1 &plusmn; INFINITY i) = NaN + NaN i
+ * tanh(&plusmn;INFINITY + i) = NaN + 0 i
+ * tanh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * tanh(0 + (&pi;/2)i) = NaN + INFINITY i</code></pre></p>
+ *
+ * @return the hyperbolic tangent of this complex number
+ * @since 1.2
+ */
+ public Complex tanh() {
+ if (isNaN()) {
+ return Complex.NaN;
+ }
+
+ double real2 = 2.0 * real;
+ double imaginary2 = 2.0 * imaginary;
+ double d = MathUtils.cosh(real2) + FastMath.cos(imaginary2);
+
+ return createComplex(MathUtils.sinh(real2) / d, FastMath.sin(imaginary2) / d);
+ }
+
+
+
+ /**
+ * <p>Compute the argument of this complex number.
+ * </p>
+ * <p>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>
+ * <p>If either real or imaginary part (or both) is NaN, NaN is returned. Infinite parts are handled
+ * as java.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 java.Math.atan2 for full details.</p>
+ *
+ * @return the argument of this complex number
+ */
+ public double getArgument() {
+ return FastMath.atan2(getImaginary(), getReal());
+ }
+
+ /**
+ * <p>Computes the n-th roots of this complex number.
+ * </p>
+ * <p>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</code></i>, where <code>abs</code> and <code>phi</code> are
+ * respectively the {@link #abs() modulus} and {@link #getArgument() argument} of this complex number.
+ * </p>
+ * <p>If one or both parts of this complex number is NaN, a list with just one element,
+ * {@link #NaN} is returned.</p>
+ * <p>if neither part is NaN, but at least one part is infinite, the result is a one-element
+ * list containing {@link #INF}.</p>
+ *
+ * @param n degree of root
+ * @return List<Complex> all nth roots of this complex number
+ * @throws IllegalArgumentException if parameter n is less than or equal to 0
+ * @since 2.0
+ */
+ public List<Complex> nthRoot(int n) throws IllegalArgumentException {
+
+ if (n <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_COMPUTE_NTH_ROOT_FOR_NEGATIVE_N,
+ n);
+ }
+
+ List<Complex> result = new ArrayList<Complex>();
+
+ if (isNaN()) {
+ result.add(Complex.NaN);
+ return result;
+ }
+
+ if (isInfinite()) {
+ result.add(Complex.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 the real part
+ * @param imaginaryPart the imaginary part
+ * @return a new complex number instance
+ * @since 1.2
+ */
+ protected Complex createComplex(double realPart, double imaginaryPart) {
+ return new Complex(realPart, imaginaryPart);
+ }
+
+ /**
+ * <p>Resolve the transient fields in a deserialized Complex Object.</p>
+ * <p>Subclasses will need to override {@link #createComplex} to deserialize properly</p>
+ * @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();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/complex/ComplexField.java b/src/main/java/org/apache/commons/math/complex/ComplexField.java
new file mode 100644
index 0000000..5bce1d5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/complex/ComplexField.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.math.complex;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+
+/**
+ * Representation of the complex numbers field.
+ * <p>
+ * This class is a singleton.
+ * </p>
+ * @see Complex
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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;
+ }
+
+ // 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 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/math/complex/ComplexFormat.java b/src/main/java/org/apache/commons/math/complex/ComplexFormat.java
new file mode 100644
index 0000000..288d6de
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/complex/ComplexFormat.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.math.complex;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.CompositeFormat;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * 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.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class ComplexFormat extends CompositeFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3343698360149467646L;
+
+ /** 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 String imaginaryCharacter;
+
+ /** The format used for the imaginary part. */
+ private NumberFormat imaginaryFormat;
+
+ /** The format used for the real part. */
+ private 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(DEFAULT_IMAGINARY_CHARACTER, getDefaultNumberFormat());
+ }
+
+ /**
+ * 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.
+ */
+ public ComplexFormat(NumberFormat format) {
+ this(DEFAULT_IMAGINARY_CHARACTER, 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.
+ */
+ public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat) {
+ this(DEFAULT_IMAGINARY_CHARACTER, realFormat, imaginaryFormat);
+ }
+
+ /**
+ * 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.
+ */
+ public ComplexFormat(String imaginaryCharacter) {
+ this(imaginaryCharacter, 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.
+ */
+ public ComplexFormat(String imaginaryCharacter, NumberFormat format) {
+ this(imaginaryCharacter, format, (NumberFormat)format.clone());
+ }
+
+ /**
+ * 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.
+ */
+ public ComplexFormat(String imaginaryCharacter, NumberFormat realFormat,
+ NumberFormat imaginaryFormat) {
+ super();
+ setImaginaryCharacter(imaginaryCharacter);
+ setImaginaryFormat(imaginaryFormat);
+ setRealFormat(realFormat);
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available.
+ * <p>This is the same set as the {@link NumberFormat} set.</p>
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This static method calls {@link #format(Object)} on a default instance of
+ * ComplexFormat.
+ *
+ * @param c Complex object to format
+ * @return A formatted number in the form "Re(c) + Im(c)i"
+ */
+ public static String formatComplex(Complex c) {
+ return getInstance().format(c);
+ }
+
+ /**
+ * 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();
+ formatDouble(re, getRealFormat(), toAppendTo, pos);
+
+ // format sign and imaginary
+ double im = complex.getImaginary();
+ if (im < 0.0) {
+ toAppendTo.append(" - ");
+ formatDouble(-im, getImaginaryFormat(), toAppendTo, pos);
+ toAppendTo.append(getImaginaryCharacter());
+ } else if (im > 0.0 || Double.isNaN(im)) {
+ toAppendTo.append(" + ");
+ formatDouble(im, getImaginaryFormat(), toAppendTo, pos);
+ toAppendTo.append(getImaginaryCharacter());
+ }
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats a object to produce a string. <code>obj</code> 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 IllegalArgumentException is <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo,
+ FieldPosition pos) {
+
+ 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 MathRuntimeException.createIllegalArgumentException(
+ 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 = getDefaultNumberFormat(locale);
+ return new ComplexFormat(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.
+ * @exception ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ public Complex parse(String source) throws ParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Complex result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw MathRuntimeException.createParseException(
+ parsePosition.getErrorIndex(),
+ LocalizedFormats.UNPARSEABLE_COMPLEX_NUMBER, source);
+ }
+ 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
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse real
+ Number re = 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 = 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
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse imaginary
+ Number im = 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 (!parseFixedstring(source, getImaginaryCharacter(), pos)) {
+ return null;
+ }
+
+ return new Complex(re.doubleValue(), im.doubleValue() * sign);
+
+ }
+
+ /**
+ * Parses a string to produce a object.
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed object.
+ * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
+ */
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return parse(source, pos);
+ }
+
+ /**
+ * Modify the imaginaryCharacter.
+ * @param imaginaryCharacter The new imaginaryCharacter value.
+ * @throws IllegalArgumentException if <code>imaginaryCharacter</code> is
+ * <code>null</code> or an empty string.
+ */
+ public void setImaginaryCharacter(String imaginaryCharacter) {
+ if (imaginaryCharacter == null || imaginaryCharacter.length() == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.EMPTY_STRING_FOR_IMAGINARY_CHARACTER);
+ }
+ this.imaginaryCharacter = imaginaryCharacter;
+ }
+
+ /**
+ * Modify the imaginaryFormat.
+ * @param imaginaryFormat The new imaginaryFormat value.
+ * @throws NullArgumentException if {@code imaginaryFormat} is {@code null}.
+ */
+ public void setImaginaryFormat(NumberFormat imaginaryFormat) {
+ if (imaginaryFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.IMAGINARY_FORMAT);
+ }
+ this.imaginaryFormat = imaginaryFormat;
+ }
+
+ /**
+ * Modify the realFormat.
+ * @param realFormat The new realFormat value.
+ * @throws NullArgumentException if {@code realFormat} is {@code null}.
+ */
+ public void setRealFormat(NumberFormat realFormat) {
+ if (realFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.REAL_FORMAT);
+ }
+ this.realFormat = realFormat;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/complex/ComplexUtils.java b/src/main/java/org/apache/commons/math/complex/ComplexUtils.java
new file mode 100644
index 0000000..f7fb392
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/complex/ComplexUtils.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.math.complex;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Static implementations of common
+ * {@link org.apache.commons.math.complex.Complex} utilities functions.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class ComplexUtils {
+
+ /**
+ * Default constructor.
+ */
+ private ComplexUtils() {
+ super();
+ }
+
+ /**
+ * 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>
+ * <p>
+ * If either <code>r</code> or <code>theta</code> is NaN, or
+ * <code>theta</code> is infinite, {@link Complex#NaN} is returned.</p>
+ * <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></p>
+ *
+ * @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 IllegalArgumentException if r is negative
+ * @since 1.1
+ */
+ public static Complex polar2Complex(double r, double theta) {
+ if (r < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_COMPLEX_MODULE, r);
+ }
+ return new Complex(r * FastMath.cos(theta), r * FastMath.sin(theta));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/complex/package.html b/src/main/java/org/apache/commons/math/complex/package.html
new file mode 100644
index 0000000..755dba0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/complex/package.html
@@ -0,0 +1,23 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Complex number type and implementations of complex transcendental
+ functions.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/dfp/Dfp.java b/src/main/java/org/apache/commons/math/dfp/Dfp.java
new file mode 100644
index 0000000..7ce338c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/dfp/Dfp.java
@@ -0,0 +1,2399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.dfp;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.FieldElement;
+
+/**
+ * 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>
+ *
+ * <p>The design goals here are:
+ * <ol>
+ * <li>Decimal math, or close to it</li>
+ * <li>Settable precision (but no mix between numbers using different settings)</li>
+ * <li>Portability. Code should be keep as portable as possible.</li>
+ * <li>Performance</li>
+ * <li>Accuracy - Results should always be +/- 1 ULP for basic
+ * algebraic operation</li>
+ * <li>Comply with IEEE 854-1987 as much as possible.
+ * (See IEEE 854-1987 notes below)</li>
+ * </ol></p>
+ *
+ * <p>Trade offs:
+ * <ol>
+ * <li>Memory foot print. I'm using more memory than necessary to
+ * represent numbers to get better performance.</li>
+ * <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.</li>
+ * </ol></p>
+ *
+ * <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>
+ *
+ * <p>IEEE 854-1987 Notes and differences</p>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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.</p>
+ * @see DfpField
+ * @version $Revision: 1003889 $ $Date: 2010-10-02 23:11:55 +0200 (sam. 02 oct. 2010) $
+ * @since 2.2
+ */
+public class Dfp implements FieldElement<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: & 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) {
+ 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;
+
+ int trailingZeros = 0;
+ while (p > q) {
+ if (striped[p] != '0') {
+ break;
+ }
+ trailingZeros++;
+ 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.math.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.
+ * </p>
+ * @return {@link org.apache.commons.math.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 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 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 + (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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ public int log10() {
+ 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 = 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 = 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 0&lt;=x&lt;radix.
+ * There are speed advantages in this special case
+ * @param x multiplicand
+ * @return product of this and x
+ */
+ public Dfp multiply(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 = 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;
+
+ }
+
+ /** Compute the square root.
+ * @return square root of the instance
+ */
+ 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 = ae % p;
+ 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 = result.exp + ERR_SCALE;
+ break;
+
+ case DfpField.FLAG_OVERFLOW:
+ result.exp = 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;
+ if (lessThan(getZero())) {
+ 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.log10() * 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;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/dfp/DfpDec.java b/src/main/java/org/apache/commons/math/dfp/DfpDec.java
new file mode 100644
index 0000000..d2c3b56
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/dfp/DfpDec.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.math.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.
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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(log10() - 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(log10());
+ 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/math/dfp/DfpField.java b/src/main/java/org/apache/commons/math/dfp/DfpField.java
new file mode 100644
index 0000000..65a25f4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/dfp/DfpField.java
@@ -0,0 +1,750 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.dfp;
+
+import org.apache.commons.math.Field;
+
+/** Field for Decimal floating point instances.
+ * @version $Revision: 995987 $ $Date: 2010-09-10 23:24:15 +0200 (ven. 10 sept. 2010) $
+ * @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;
+
+ /** 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.
+ * </p>
+ * @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.
+ * </p>
+ * @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)}
+ * </p>
+ * @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;
+ }
+
+ /** 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).
+ *
+ * Let f(x) = ln(x),
+ *
+ * We know that f'(x) = 1/x, thus from Taylor's theorem we have:
+ *
+ * ----- n+1 n
+ * f(x) = \ (-1) (x - 1)
+ * / ---------------- for 1 <= n <= infinity
+ * ----- n
+ *
+ * or
+ * 2 3 4
+ * (x-1) (x-1) (x-1)
+ * ln(x) = (x-1) - ----- + ------ - ------ + ...
+ * 2 3 4
+ *
+ * alternatively,
+ *
+ * 2 3 4
+ * x x x
+ * ln(x+1) = x - - + - - - + ...
+ * 2 3 4
+ *
+ * This series can be used to compute ln(x), but it converges too slowly.
+ *
+ * If we substitute -x for x above, we get
+ *
+ * 2 3 4
+ * x x x
+ * ln(1-x) = -x - - - - - - + ...
+ * 2 3 4
+ *
+ * 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
+ *
+ * 3 5 7
+ * 2x 2x 2x
+ * ln(x+1) - ln(x-1) = 2x + --- + --- + ---- + ...
+ * 3 5 7
+ *
+ * By the property of logarithms that ln(a) - ln(b) = ln (a/b) we have:
+ *
+ * 3 5 7
+ * x+1 / x x x \
+ * ln ----- = 2 * | x + ---- + ---- + ---- + ... |
+ * x-1 \ 3 5 7 /
+ *
+ * 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 = 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/math/dfp/DfpMath.java b/src/main/java/org/apache/commons/math/dfp/DfpMath.java
new file mode 100644
index 0000000..56af1d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/dfp/DfpMath.java
@@ -0,0 +1,969 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.dfp;
+
+/** Mathematical routines for use with {@link Dfp}.
+ * The constants are defined in {@link DfpField}
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ * @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 = 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 = trial * 2;
+ } while (a>trial);
+
+ r = prevr;
+ trial = prevtrial;
+
+ a = 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),
+ *
+ * We know that f'(x) = 1/x, thus from Taylor's theorum we have:
+ *
+ * ----- n+1 n
+ * f(x) = \ (-1) (x - 1)
+ * / ---------------- for 1 <= n <= infinity
+ * ----- n
+ *
+ * or
+ * 2 3 4
+ * (x-1) (x-1) (x-1)
+ * ln(x) = (x-1) - ----- + ------ - ------ + ...
+ * 2 3 4
+ *
+ * alternatively,
+ *
+ * 2 3 4
+ * x x x
+ * ln(x+1) = x - - + - - - + ...
+ * 2 3 4
+ *
+ * This series can be used to compute ln(x), but it converges too slowly.
+ *
+ * If we substitute -x for x above, we get
+ *
+ * 2 3 4
+ * x x x
+ * ln(1-x) = -x - - - - - - + ...
+ * 2 3 4
+ *
+ * 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
+ *
+ * 3 5 7
+ * 2x 2x 2x
+ * ln(x+1) - ln(x-1) = 2x + --- + --- + ---- + ...
+ * 3 5 7
+ *
+ * By the property of logarithms that ln(a) - ln(b) = ln (a/b) we have:
+ *
+ * 3 5 7
+ * x+1 / x x x \
+ * ln ----- = 2 * | x + ---- + ---- + ---- + ... |
+ * x-1 \ 3 5 7 /
+ *
+ * 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 = 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) {
+ // if y is odd integer
+ if (y.rint().equals(y) && !y.remainder(two).equals(zero)) {
+ 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))) {
+ Dfp c[] = new Dfp[2];
+ c[0] = x;
+ c[1] = zero;
+
+ //y = sinInternal(c);
+ 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
+ *
+ * Uses the typical taylor series
+ *
+ * but may reduce arguments using the following identity
+ * tan(x+y) = (tan(x) + tan(y)) / (1 - tan(x)*tan(y))
+ *
+ * since tan(PI/8) = sqrt(2)-1,
+ *
+ * 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/math/dfp/package.html b/src/main/java/org/apache/commons/math/dfp/package.html
new file mode 100644
index 0000000..f63dd6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/dfp/package.html
@@ -0,0 +1,88 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 992696 $ $Date: 2010-09-05 00:57:31 +0200 (dim. 05 sept. 2010) $ -->
+ <body>
+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>
+
+<p>The design goals here are:
+<ol>
+ <li>Decimal math, or close to it</li>
+ <li>Settable precision (but no mix between numbers using different settings)</li>
+ <li>Portability. Code should be keep as portable as possible.</li>
+ <li>Performance</li>
+ <li>Accuracy - Results should always be +/- 1 ULP for basic
+ algebraic operation</li>
+ <li>Comply with IEEE 854-1987 as much as possible.
+ (See IEEE 854-1987 notes below)</li>
+</ol></p>
+
+<p>Trade offs:
+<ol>
+ <li>Memory foot print. I'm using more memory than necessary to
+ represent numbers to get better performance.</li>
+ <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.</li>
+</ol></p>
+
+<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>
+
+<p>IEEE 854-1987 Notes and differences</p>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<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.</p>
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java b/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java
new file mode 100644
index 0000000..aaa2efd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.solvers.BrentSolver;
+import org.apache.commons.math.analysis.solvers.UnivariateRealSolverUtils;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomDataImpl;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Base class for continuous distributions. Default implementations are
+ * provided for some of the methods that do not vary from distribution to
+ * distribution.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ */
+public abstract class AbstractContinuousDistribution
+ extends AbstractDistribution
+ implements ContinuousDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -38038050983108802L;
+
+ /**
+ * RandomData instance used to generate samples from the distribution
+ * @since 2.2
+ */
+ protected final RandomDataImpl randomData = new RandomDataImpl();
+
+ /**
+ * Solver absolute accuracy for inverse cumulative computation
+ * @since 2.1
+ */
+ private double solverAbsoluteAccuracy = BrentSolver.DEFAULT_ABSOLUTE_ACCURACY;
+
+ /**
+ * Default constructor.
+ */
+ protected AbstractContinuousDistribution() {
+ super();
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @throws MathRuntimeException if the specialized class hasn't implemented this function
+ * @since 2.1
+ */
+ public double density(double x) throws MathRuntimeException {
+ throw new MathRuntimeException(new UnsupportedOperationException(),
+ LocalizedFormats.NO_DENSITY_FOR_THIS_DISTRIBUTION);
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ }
+
+ // by default, do simple root finding using bracketing and default solver.
+ // subclasses can override if there is a better method.
+ UnivariateRealFunction rootFindingFunction =
+ new UnivariateRealFunction() {
+ public double value(double x) throws FunctionEvaluationException {
+ double ret = Double.NaN;
+ try {
+ ret = cumulativeProbability(x) - p;
+ } catch (MathException ex) {
+ throw new FunctionEvaluationException(x, ex.getSpecificPattern(), ex.getGeneralPattern(), ex.getArguments());
+ }
+ if (Double.isNaN(ret)) {
+ throw new FunctionEvaluationException(x, LocalizedFormats.CUMULATIVE_PROBABILITY_RETURNED_NAN, x, p);
+ }
+ return ret;
+ }
+ };
+
+ // Try to bracket root, test domain endpoints if this fails
+ double lowerBound = getDomainLowerBound(p);
+ double upperBound = getDomainUpperBound(p);
+ double[] bracket = null;
+ try {
+ bracket = UnivariateRealSolverUtils.bracket(
+ rootFindingFunction, getInitialDomain(p),
+ lowerBound, upperBound);
+ } catch (ConvergenceException ex) {
+ /*
+ * Check domain endpoints to see if one gives value that is within
+ * the default solver's defaultAbsoluteAccuracy of 0 (will be the
+ * case if density has bounded support and p is 0 or 1).
+ */
+ if (FastMath.abs(rootFindingFunction.value(lowerBound)) < getSolverAbsoluteAccuracy()) {
+ return lowerBound;
+ }
+ if (FastMath.abs(rootFindingFunction.value(upperBound)) < getSolverAbsoluteAccuracy()) {
+ return upperBound;
+ }
+ // Failed bracket convergence was not because of corner solution
+ throw new MathException(ex);
+ }
+
+ // find root
+ double root = UnivariateRealSolverUtils.solve(rootFindingFunction,
+ // override getSolverAbsoluteAccuracy() to use a Brent solver with
+ // absolute accuracy different from BrentSolver default
+ bracket[0],bracket[1], getSolverAbsoluteAccuracy());
+ return root;
+ }
+
+ /**
+ * Reseeds the random generator used to generate samples.
+ *
+ * @param seed the new seed
+ * @since 2.2
+ */
+ public void reseedRandomGenerator(long seed) {
+ randomData.reSeed(seed);
+ }
+
+ /**
+ * Generates a random value sampled from this distribution. The default
+ * implementation uses the
+ * <a href="http://en.wikipedia.org/wiki/Inverse_transform_sampling"> inversion method.</a>
+ *
+ * @return random value
+ * @since 2.2
+ * @throws MathException if an error occurs generating the random value
+ */
+ public double sample() throws MathException {
+ return randomData.nextInversionDeviate(this);
+ }
+
+ /**
+ * Generates a random sample from the distribution. The default implementation
+ * generates the sample by calling {@link #sample()} in a loop.
+ *
+ * @param sampleSize number of random values to generate
+ * @since 2.2
+ * @return an array representing the random sample
+ * @throws MathException if an error occurs generating the sample
+ * @throws IllegalArgumentException if sampleSize is not positive
+ */
+ public double[] sample(int sampleSize) throws MathException {
+ if (sampleSize <= 0) {
+ MathRuntimeException.createIllegalArgumentException(LocalizedFormats.NOT_POSITIVE_SAMPLE_SIZE, sampleSize);
+ }
+ double[] out = new double[sampleSize];
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+ return out;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ protected abstract double getInitialDomain(double p);
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ protected abstract double getDomainLowerBound(double p);
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ protected abstract double getDomainUpperBound(double p);
+
+ /**
+ * Returns the solver absolute accuracy for inverse cumulative computation.
+ *
+ * @return the maximum absolute error in inverse cumulative probability estimates
+ * @since 2.1
+ */
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/AbstractDistribution.java b/src/main/java/org/apache/commons/math/distribution/AbstractDistribution.java
new file mode 100644
index 0000000..c32ba29
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/AbstractDistribution.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Base class for probability distributions.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public abstract class AbstractDistribution
+ implements Distribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -38038050983108802L;
+
+ /**
+ * Default constructor.
+ */
+ protected AbstractDistribution() {
+ super();
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(x0 &le; X &le; x1).
+ * <p>
+ * The default implementation uses the identity</p>
+ * <p>
+ * P(x0 &le; X &le; x1) = P(X &le; x1) - P(X &le; x0) </p>
+ *
+ * @param x0 the (inclusive) 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</code> and <code>x1</code>,
+ * including the endpoints.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>x0 > x1</code>
+ */
+ public double cumulativeProbability(double x0, double x1)
+ throws MathException {
+ if (x0 > x1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT,
+ x0, x1);
+ }
+ return cumulativeProbability(x1) - cumulativeProbability(x0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java b/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java
new file mode 100644
index 0000000..96cfe5d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomDataImpl;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * Base class for integer-valued discrete distributions. Default
+ * implementations are provided for some of the methods that do not vary
+ * from distribution to distribution.
+ *
+ * @version $Revision: 1067494 $ $Date: 2011-02-05 20:49:07 +0100 (sam. 05 févr. 2011) $
+ */
+public abstract class AbstractIntegerDistribution extends AbstractDistribution
+ implements IntegerDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1146319659338487221L;
+
+ /**
+ * RandomData instance used to generate samples from the distribution
+ * @since 2.2
+ */
+ protected final RandomDataImpl randomData = new RandomDataImpl();
+
+ /**
+ * Default constructor.
+ */
+ protected AbstractIntegerDistribution() {
+ super();
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X &le; x). In other words,
+ * this method represents the (cumulative) distribution function, or
+ * CDF, for this distribution.
+ * <p>
+ * If <code>x</code> does not represent an integer value, the CDF is
+ * evaluated at the greatest integer less than x.
+ *
+ * @param x the value at which the distribution function is evaluated.
+ * @return cumulative probability that a random variable with this
+ * distribution takes a value less than or equal to <code>x</code>
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException {
+ return cumulativeProbability((int) FastMath.floor(x));
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(x0 &le; X &le; x1).
+ *
+ * @param x0 the (inclusive) 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</code> and <code>x1</code>,
+ * including the endpoints.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>x0 > x1</code>
+ */
+ @Override
+ public double cumulativeProbability(double x0, double x1)
+ throws MathException {
+ if (x0 > x1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1);
+ }
+ if (FastMath.floor(x0) < x0) {
+ return cumulativeProbability(((int) FastMath.floor(x0)) + 1,
+ (int) FastMath.floor(x1)); // don't want to count mass below x0
+ } else { // x0 is mathematical integer, so use as is
+ return cumulativeProbability((int) FastMath.floor(x0),
+ (int) FastMath.floor(x1));
+ }
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X &le; x). In other words,
+ * this method represents the probability distribution function, or PDF,
+ * for this distribution.
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return PDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public abstract double cumulativeProbability(int x) throws MathException;
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X = x). In other words, this
+ * method represents the probability mass function, or PMF, for the distribution.
+ * <p>
+ * If <code>x</code> does not represent an integer value, 0 is returned.
+ *
+ * @param x the value at which the probability density function is evaluated
+ * @return the value of the probability density function at x
+ */
+ public double probability(double x) {
+ double fl = FastMath.floor(x);
+ if (fl == x) {
+ return this.probability((int) x);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(x0 &le; X &le; x1).
+ *
+ * @param x0 the inclusive, lower bound
+ * @param x1 the inclusive, upper bound
+ * @return the cumulative probability.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if x0 > x1
+ */
+ public double cumulativeProbability(int x0, int x1) throws MathException {
+ if (x0 > x1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1);
+ }
+ return cumulativeProbability(x1) - cumulativeProbability(x0 - 1);
+ }
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns the largest x, such
+ * that P(X &le; x) &le; <code>p</code>.
+ *
+ * @param p the desired probability
+ * @return the largest x such that P(X &le; x) <= p
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if p < 0 or p > 1
+ */
+ public int inverseCumulativeProbability(final double p) throws MathException{
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ }
+
+ // by default, do simple bisection.
+ // subclasses can override if there is a better method.
+ int x0 = getDomainLowerBound(p);
+ int x1 = getDomainUpperBound(p);
+ double pm;
+ while (x0 < x1) {
+ int xm = x0 + (x1 - x0) / 2;
+ pm = checkedCumulativeProbability(xm);
+ if (pm > p) {
+ // update x1
+ if (xm == x1) {
+ // this can happen with integer division
+ // simply decrement x1
+ --x1;
+ } else {
+ // update x1 normally
+ x1 = xm;
+ }
+ } else {
+ // update x0
+ if (xm == x0) {
+ // this can happen with integer division
+ // simply increment x0
+ ++x0;
+ } else {
+ // update x0 normally
+ x0 = xm;
+ }
+ }
+ }
+
+ // insure x0 is the correct critical point
+ pm = checkedCumulativeProbability(x0);
+ while (pm > p) {
+ --x0;
+ pm = checkedCumulativeProbability(x0);
+ }
+
+ return x0;
+ }
+
+ /**
+ * Reseeds the random generator used to generate samples.
+ *
+ * @param seed the new seed
+ * @since 2.2
+ */
+ public void reseedRandomGenerator(long seed) {
+ randomData.reSeed(seed);
+ }
+
+ /**
+ * Generates a random value sampled from this distribution. The default
+ * implementation uses the
+ * <a href="http://en.wikipedia.org/wiki/Inverse_transform_sampling"> inversion method.</a>
+ *
+ * @return random value
+ * @since 2.2
+ * @throws MathException if an error occurs generating the random value
+ */
+ public int sample() throws MathException {
+ return randomData.nextInversionDeviate(this);
+ }
+
+ /**
+ * Generates a random sample from the distribution. The default implementation
+ * generates the sample by calling {@link #sample()} in a loop.
+ *
+ * @param sampleSize number of random values to generate
+ * @since 2.2
+ * @return an array representing the random sample
+ * @throws MathException if an error occurs generating the sample
+ * @throws IllegalArgumentException if sampleSize is not positive
+ */
+ public int[] sample(int sampleSize) throws MathException {
+ if (sampleSize <= 0) {
+ MathRuntimeException.createIllegalArgumentException(LocalizedFormats.NOT_POSITIVE_SAMPLE_SIZE, 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 NaN values returned.
+ * Throws MathException if the value is NaN. Rethrows any MathException encountered
+ * evaluating the cumulative probability function. Throws
+ * MathException if the cumulative probability function returns NaN.
+ *
+ * @param argument input value
+ * @return cumulative probability
+ * @throws MathException if the cumulative probability is NaN
+ */
+ private double checkedCumulativeProbability(int argument) throws MathException {
+ double result = Double.NaN;
+ result = cumulativeProbability(argument);
+ if (Double.isNaN(result)) {
+ throw new MathException(LocalizedFormats.DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN, argument);
+ }
+ return result;
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a PDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ protected abstract int getDomainLowerBound(double p);
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a PDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ protected abstract int getDomainUpperBound(double p);
+
+ /**
+ * Use this method to get information about whether the lower bound
+ * of the support is inclusive or not. For discrete support,
+ * only true here is meaningful.
+ *
+ * @return true (always but at Integer.MIN_VALUE because of the nature of discrete support)
+ * @since 2.2
+ */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * Use this method to get information about whether the upper bound
+ * of the support is inclusive or not. For discrete support,
+ * only true here is meaningful.
+ *
+ * @return true (always but at Integer.MAX_VALUE because of the nature of discrete support)
+ * @since 2.2
+ */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/BetaDistribution.java b/src/main/java/org/apache/commons/math/distribution/BetaDistribution.java
new file mode 100644
index 0000000..7693b04
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/BetaDistribution.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * Computes the cumulative, inverse cumulative and density functions for the beta distribuiton.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta_distribution</a>
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ * @since 2.0
+ */
+public interface BetaDistribution extends ContinuousDistribution, HasDensity<Double> {
+ /**
+ * Modify the shape parameter, alpha.
+ * @param alpha the new shape parameter.
+ * @deprecated as of 2.1
+ */
+ @Deprecated
+ void setAlpha(double alpha);
+
+ /**
+ * Access the shape parameter, alpha
+ * @return alpha.
+ */
+ double getAlpha();
+
+ /**
+ * Modify the shape parameter, beta.
+ * @param beta the new scale parameter.
+ * @deprecated as of 2.1
+ */
+ @Deprecated
+ void setBeta(double beta);
+
+ /**
+ * Access the shape parameter, beta
+ * @return beta.
+ */
+ double getBeta();
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @exception MathException if probability density cannot be computed
+ */
+ double density(Double x) throws MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/BetaDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/BetaDistributionImpl.java
new file mode 100644
index 0000000..4d96187
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/BetaDistributionImpl.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.distribution;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.special.Beta;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the Beta distribution.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Beta_distribution">
+ * Beta distribution</a></li>
+ * </ul>
+ * </p>
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ * @since 2.0
+ */
+public class BetaDistributionImpl
+ extends AbstractContinuousDistribution implements BetaDistribution {
+
+ /**
+ * 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 double alpha;
+
+ /** Second shape parameter. */
+ private 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.
+ * @param alpha first shape parameter (must be positive)
+ * @param beta second shape parameter (must be positive)
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
+ * @since 2.1
+ */
+ public BetaDistributionImpl(double alpha, double beta, double inverseCumAccuracy) {
+ this.alpha = alpha;
+ this.beta = beta;
+ z = Double.NaN;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Build a new instance.
+ * @param alpha first shape parameter (must be positive)
+ * @param beta second shape parameter (must be positive)
+ */
+ public BetaDistributionImpl(double alpha, double beta) {
+ this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setAlpha(double alpha) {
+ this.alpha = alpha;
+ z = Double.NaN;
+ }
+
+ /** {@inheritDoc} */
+ public double getAlpha() {
+ return alpha;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setBeta(double beta) {
+ this.beta = beta;
+ z = Double.NaN;
+ }
+
+ /** {@inheritDoc} */
+ 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);
+ }
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @deprecated
+ */
+ @Deprecated
+ public double density(Double x) {
+ return density(x.doubleValue());
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ recomputeZ();
+ if (x < 0 || x > 1) {
+ return 0;
+ } else if (x == 0) {
+ if (alpha < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA, alpha);
+ }
+ return 0;
+ } else if (x == 1) {
+ if (beta < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA, beta);
+ }
+ return 0;
+ } else {
+ double logX = FastMath.log(x);
+ double log1mX = FastMath.log1p(-x);
+ return FastMath.exp((alpha - 1) * logX + (beta - 1) * log1mX - z);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(double p) throws MathException {
+ if (p == 0) {
+ return 0;
+ } else if (p == 1) {
+ return 1;
+ } else {
+ return super.inverseCumulativeProbability(p);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getInitialDomain(double p) {
+ return p;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) throws MathException {
+ if (x <= 0) {
+ return 0;
+ } else if (x >= 1) {
+ return 1;
+ } else {
+ return Beta.regularizedBeta(x, alpha, beta);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double cumulativeProbability(double x0, double x1) throws MathException {
+ return cumulativeProbability(x1) - cumulativeProbability(x0);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for this distribution.
+ * The support of the Beta distribution is always [0, 1], regardless
+ * of the parameters, so this method always returns 0.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for this distribution.
+ * The support of the Beta distribution is always [0, 1], regardless
+ * of the parameters, so this method always returns 1.
+ *
+ * @return lower bound of the support (always 1)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return 1;
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For first shape parameter <code>s1</code> and
+ * second shape parameter <code>s2</code>, the mean is
+ * <code>s1 / (s1 + s2)</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ final double a = getAlpha();
+ return a / (a + getBeta());
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For first shape parameter <code>s1</code> and
+ * second shape parameter <code>s2</code>,
+ * the variance is
+ * <code>[ s1 * s2 ] / [ (s1 + s2)^2 * (s1 + s2 + 1) ]</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double a = getAlpha();
+ final double b = getBeta();
+ final double alphabetasum = a + b;
+ return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/BinomialDistribution.java b/src/main/java/org/apache/commons/math/distribution/BinomialDistribution.java
new file mode 100644
index 0000000..94b3236
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/BinomialDistribution.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.math.distribution;
+
+/**
+ * The Binomial Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/BinomialDistribution.html">
+ * Binomial Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface BinomialDistribution extends IntegerDistribution {
+ /**
+ * Access the number of trials for this distribution.
+ * @return the number of trials.
+ */
+ int getNumberOfTrials();
+
+ /**
+ * Access the probability of success for this distribution.
+ * @return the probability of success.
+ */
+ double getProbabilityOfSuccess();
+
+ /**
+ * Change the number of trials for this distribution.
+ * @param trials the new number of trials.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setNumberOfTrials(int trials);
+
+ /**
+ * Change the probability of success for this distribution.
+ * @param p the new probability of success.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setProbabilityOfSuccess(double p);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/BinomialDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/BinomialDistributionImpl.java
new file mode 100644
index 0000000..9ebb629
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/BinomialDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Beta;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * The default implementation of {@link BinomialDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class BinomialDistributionImpl extends AbstractIntegerDistribution
+ implements BinomialDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 6751309484392813623L;
+
+ /** The number of trials. */
+ private int numberOfTrials;
+
+ /** The probability of success. */
+ private double probabilityOfSuccess;
+
+ /**
+ * Create a binomial distribution with the given number of trials and
+ * probability of success.
+ *
+ * @param trials the number of trials.
+ * @param p the probability of success.
+ */
+ public BinomialDistributionImpl(int trials, double p) {
+ super();
+ setNumberOfTrialsInternal(trials);
+ setProbabilityOfSuccessInternal(p);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Change the number of trials for this distribution.
+ *
+ * @param trials the new number of trials.
+ * @throws IllegalArgumentException if <code>trials</code> is not a valid
+ * number of trials.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNumberOfTrials(int trials) {
+ setNumberOfTrialsInternal(trials);
+ }
+
+ /**
+ * Change the number of trials for this distribution.
+ *
+ * @param trials the new number of trials.
+ * @throws IllegalArgumentException if <code>trials</code> is not a valid
+ * number of trials.
+ */
+ private void setNumberOfTrialsInternal(int trials) {
+ if (trials < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_NUMBER_OF_TRIALS, trials);
+ }
+ numberOfTrials = trials;
+ }
+
+ /**
+ * Change the probability of success for this distribution.
+ *
+ * @param p the new probability of success.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setProbabilityOfSuccess(double p) {
+ setProbabilityOfSuccessInternal(p);
+ }
+
+ /**
+ * Change the probability of success for this distribution.
+ *
+ * @param p the new probability of success.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ private void setProbabilityOfSuccessInternal(double p) {
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ }
+ probabilityOfSuccess = p;
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e. P(X &lt; <i>lower bound</i>) &lt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainLowerBound(double p) {
+ return -1;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e. P(X &lt; <i>upper bound</i>) &gt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainUpperBound(double p) {
+ return numberOfTrials;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &le; x).
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return PDF for this distribution.
+ * @throws MathException if the cumulative probability can not be computed
+ * due to convergence or other numerical errors.
+ */
+ @Override
+ public double cumulativeProbability(int x) throws MathException {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else if (x >= numberOfTrials) {
+ ret = 1.0;
+ } else {
+ ret = 1.0 - Beta.regularizedBeta(getProbabilityOfSuccess(),
+ x + 1.0, numberOfTrials - x);
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X = x).
+ *
+ * @param x the value at which the PMF is evaluated.
+ * @return PMF for this distribution.
+ */
+ public double probability(int x) {
+ double ret;
+ if (x < 0 || x > numberOfTrials) {
+ ret = 0.0;
+ } else {
+ ret = FastMath.exp(SaddlePointExpansion.logBinomialProbability(x,
+ numberOfTrials, probabilityOfSuccess,
+ 1.0 - probabilityOfSuccess));
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns the largest x, such that
+ * P(X &le; x) &le; <code>p</code>.
+ * <p>
+ * Returns <code>-1</code> for p=0 and <code>Integer.MAX_VALUE</code> for
+ * p=1.
+ * </p>
+ *
+ * @param p the desired probability
+ * @return the largest x such that P(X &le; x) <= p
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if p < 0 or p > 1
+ */
+ @Override
+ public int inverseCumulativeProbability(final double p)
+ throws MathException {
+ // handle extreme values explicitly
+ if (p == 0) {
+ return -1;
+ }
+ if (p == 1) {
+ return Integer.MAX_VALUE;
+ }
+
+ // use default bisection impl
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0 no matter the number of trials
+ * and probability parameter.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is the number of trials.
+ *
+ * @return upper bound of the support (equal to number of trials)
+ * @since 2.2
+ */
+ public int getSupportUpperBound() {
+ return getNumberOfTrials();
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For <code>n</code> number of trials and
+ * probability parameter <code>p</code>, the mean is
+ * <code>n * p</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ return (double)getNumberOfTrials() * getProbabilityOfSuccess();
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For <code>n</code> number of trials and
+ * probability parameter <code>p</code>, the variance is
+ * <code>n * p * (1 - p)</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double p = getProbabilityOfSuccess();
+ return (double)getNumberOfTrials() * p * (1 - p);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/CauchyDistribution.java b/src/main/java/org/apache/commons/math/distribution/CauchyDistribution.java
new file mode 100644
index 0000000..7a4ccbd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/CauchyDistribution.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.math.distribution;
+
+/**
+ * Cauchy Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/CauchyDistribution.html">
+ * Cauchy Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @since 1.1
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface CauchyDistribution extends ContinuousDistribution {
+
+ /**
+ * Access the median.
+ * @return median for this distribution
+ */
+ double getMedian();
+
+ /**
+ * Access the scale parameter.
+ * @return scale parameter for this distribution
+ */
+ double getScale();
+
+ /**
+ * Modify the median.
+ * @param median for this distribution
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setMedian(double median);
+
+ /**
+ * Modify the scale parameter.
+ * @param s scale parameter for this distribution
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setScale(double s);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/CauchyDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/CauchyDistributionImpl.java
new file mode 100644
index 0000000..b076924
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/CauchyDistributionImpl.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Default implementation of
+ * {@link org.apache.commons.math.distribution.CauchyDistribution}.
+ *
+ * @since 1.1
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class CauchyDistributionImpl extends AbstractContinuousDistribution
+ implements CauchyDistribution, Serializable {
+
+ /**
+ * 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 double median = 0;
+
+ /** The scale of this distribution. */
+ private double scale = 1;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Creates cauchy distribution with the medain equal to zero and scale
+ * equal to one.
+ */
+ public CauchyDistributionImpl(){
+ this(0.0, 1.0);
+ }
+
+ /**
+ * Create a cauchy distribution using the given median and scale.
+ * @param median median for this distribution
+ * @param s scale parameter for this distribution
+ */
+ public CauchyDistributionImpl(double median, double s){
+ this(median, s, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a cauchy distribution using the given median and scale.
+ * @param median median for this distribution
+ * @param s scale parameter for this distribution
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
+ * @since 2.1
+ */
+ public CauchyDistributionImpl(double median, double s, double inverseCumAccuracy) {
+ super();
+ setMedianInternal(median);
+ setScaleInternal(s);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; <code>x</code>).
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF evaluated at <code>x</code>.
+ */
+ public double cumulativeProbability(double x) {
+ return 0.5 + (FastMath.atan((x - median) / scale) / FastMath.PI);
+ }
+
+ /**
+ * Access the median.
+ * @return median for this distribution
+ */
+ public double getMedian() {
+ return median;
+ }
+
+ /**
+ * Access the scale parameter.
+ * @return scale parameter for this distribution
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ final double dev = x - median;
+ return (1 / FastMath.PI) * (scale / (dev * dev + scale * scale));
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and
+ * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) {
+ double ret;
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ } 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;
+ }
+
+ /**
+ * Modify the median.
+ * @param median for this distribution
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setMedian(double median) {
+ setMedianInternal(median);
+ }
+
+ /**
+ * Modify the median.
+ * @param newMedian for this distribution
+ */
+ private void setMedianInternal(double newMedian) {
+ this.median = newMedian;
+ }
+
+ /**
+ * Modify the scale parameter.
+ * @param s scale parameter for this distribution
+ * @throws IllegalArgumentException if <code>sd</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setScale(double s) {
+ setScaleInternal(s);
+ }
+
+ /**
+ * Modify the scale parameter.
+ * @param s scale parameter for this distribution
+ * @throws IllegalArgumentException if <code>sd</code> is not positive.
+ */
+ private void setScaleInternal(double s) {
+ if (s <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_SCALE, s);
+ }
+ scale = s;
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = -Double.MAX_VALUE;
+ } else {
+ ret = median;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = median;
+ } else {
+ ret = Double.MAX_VALUE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = median - scale;
+ } else if (p > .5) {
+ ret = median + scale;
+ } else {
+ ret = median;
+ }
+
+ 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for this distribution.
+ * The lower bound of the support of the Cauchy distribution is always
+ * negative infinity, regardless of the parameters.
+ *
+ * @return lower bound of the support (always Double.NEGATIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * Returns the upper bound of the support for this distribution.
+ * The upper bound of the support of the Cauchy distribution is always
+ * positive infinity, regardless of the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * The mean is always undefined, regardless of the parameters.
+ *
+ * @return mean (always Double.NaN)
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * The variance is always undefined, regardless of the parameters.
+ *
+ * @return variance (always Double.NaN)
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ return Double.NaN;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java b/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistribution.java
new file mode 100644
index 0000000..0478db1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistribution.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.math.distribution;
+
+/**
+ * The Chi-Squared Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">
+ * Chi-Squared Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface ChiSquaredDistribution extends ContinuousDistribution, HasDensity<Double> {
+ /**
+ * Modify the degrees of freedom.
+ * @param degreesOfFreedom the new degrees of freedom.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setDegreesOfFreedom(double degreesOfFreedom);
+
+ /**
+ * Access the degrees of freedom.
+ * @return the degrees of freedom.
+ */
+ double getDegreesOfFreedom();
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ */
+ double density(Double x);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.java
new file mode 100644
index 0000000..f877792
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ChiSquaredDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * The default implementation of {@link ChiSquaredDistribution}
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class ChiSquaredDistributionImpl
+ extends AbstractContinuousDistribution
+ implements ChiSquaredDistribution, Serializable {
+
+ /**
+ * 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 GammaDistribution gamma;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom.
+ * @param df degrees of freedom.
+ */
+ public ChiSquaredDistributionImpl(double df) {
+ this(df, new GammaDistributionImpl(df / 2.0, 2.0));
+ }
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom.
+ * @param df degrees of freedom.
+ * @param g the underlying gamma distribution used to compute probabilities.
+ * @since 1.2
+ * @deprecated as of 2.1 (to avoid possibly inconsistent state, the
+ * "GammaDistribution" will be instantiated internally)
+ */
+ @Deprecated
+ public ChiSquaredDistributionImpl(double df, GammaDistribution g) {
+ super();
+ setGammaInternal(g);
+ setDegreesOfFreedomInternal(df);
+ solverAbsoluteAccuracy = DEFAULT_INVERSE_ABSOLUTE_ACCURACY;
+ }
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom and
+ * inverse cumulative probability accuracy.
+ * @param df 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 ChiSquaredDistributionImpl(double df, double inverseCumAccuracy) {
+ super();
+ gamma = new GammaDistributionImpl(df / 2.0, 2.0);
+ setDegreesOfFreedomInternal(df);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Modify the degrees of freedom.
+ * @param degreesOfFreedom the new degrees of freedom.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setDegreesOfFreedom(double degreesOfFreedom) {
+ setDegreesOfFreedomInternal(degreesOfFreedom);
+ }
+ /**
+ * Modify the degrees of freedom.
+ * @param degreesOfFreedom the new degrees of freedom.
+ */
+ private void setDegreesOfFreedomInternal(double degreesOfFreedom) {
+ gamma.setAlpha(degreesOfFreedom / 2.0);
+ }
+
+ /**
+ * Access the degrees of freedom.
+ * @return the degrees of freedom.
+ */
+ public double getDegreesOfFreedom() {
+ return gamma.getAlpha() * 2.0;
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @deprecated
+ */
+ @Deprecated
+ public double density(Double x) {
+ return density(x.doubleValue());
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ return gamma.density(x);
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; x).
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException {
+ return gamma.cumulativeProbability(x);
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p == 0) {
+ return 0d;
+ }
+ if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return Double.MIN_VALUE * gamma.getBeta();
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ // NOTE: chi squared is skewed to the left
+ // NOTE: therefore, P(X < &mu;) > .5
+
+ double ret;
+
+ if (p < .5) {
+ // use mean
+ ret = getDegreesOfFreedom();
+ } else {
+ // use max
+ ret = Double.MAX_VALUE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ // NOTE: chi squared is skewed to the left
+ // NOTE: therefore, P(X < &mu;) > .5
+
+ double ret;
+
+ if (p < .5) {
+ // use 1/2 mean
+ ret = getDegreesOfFreedom() * .5;
+ } else {
+ // use mean
+ ret = getDegreesOfFreedom();
+ }
+
+ return ret;
+ }
+
+ /**
+ * Modify the underlying gamma distribution. The caller is responsible for
+ * insuring the gamma distribution has the proper parameter settings.
+ * @param g the new distribution.
+ * @since 1.2 made public
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setGamma(GammaDistribution g) {
+ setGammaInternal(g);
+ }
+ /**
+ * Modify the underlying gamma distribution. The caller is responsible for
+ * insuring the gamma distribution has the proper parameter settings.
+ * @param g the new distribution.
+ * @since 1.2 made public
+ */
+ private void setGammaInternal(GammaDistribution g) {
+ this.gamma = g;
+
+ }
+
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0 no matter the
+ * degrees of freedom.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound for the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity no matter the
+ * degrees of freedom.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean of the distribution.
+ *
+ * For <code>k</code> degrees of freedom, the mean is
+ * <code>k</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ return getDegreesOfFreedom();
+ }
+
+ /**
+ * Returns the variance of the distribution.
+ *
+ * For <code>k</code> degrees of freedom, the variance is
+ * <code>2 * k</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ return 2*getDegreesOfFreedom();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ContinuousDistribution.java b/src/main/java/org/apache/commons/math/distribution/ContinuousDistribution.java
new file mode 100644
index 0000000..afcd4c3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ContinuousDistribution.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * <p>Base interface for continuous distributions.</p>
+ *
+ * <p>Note: this interface will be extended in version 3.0 to include
+ * <br/><code>public double density(double x)</code><br/>
+ * that is, from version 3.0 forward, continuous distributions <strong>must</strong>
+ * include implementations of probability density functions. As of version
+ * 2.1, all continuous distribution implementations included in commons-math
+ * provide implementations of this method.</p>
+ *
+ * @version $Revision: 924362 $ $Date: 2010-03-17 17:45:31 +0100 (mer. 17 mars 2010) $
+ */
+public interface ContinuousDistribution extends Distribution {
+
+ /**
+ * For this distribution, X, this method returns x such that P(X &lt; x) = p.
+ * @param p the cumulative probability.
+ * @return x.
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ double inverseCumulativeProbability(double p) throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/DiscreteDistribution.java b/src/main/java/org/apache/commons/math/distribution/DiscreteDistribution.java
new file mode 100644
index 0000000..d6ea444
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/DiscreteDistribution.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.math.distribution;
+
+
+/**
+ * Base interface for discrete distributions.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public interface DiscreteDistribution extends Distribution {
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X = x). In other words, this
+ * method represents the probability mass function, or PMF for the distribution.
+ *
+ * @param x the value at which the probability mass function is evaluated.
+ * @return the value of the probability mass function at x
+ */
+ double probability(double x);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/Distribution.java b/src/main/java/org/apache/commons/math/distribution/Distribution.java
new file mode 100644
index 0000000..221aeb7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/Distribution.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * Base interface for probability distributions.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public interface Distribution {
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X &le; x). In other words,
+ * this method represents the (cumulative) distribution function, or
+ * CDF, for this distribution.
+ *
+ * @param x the value at which the distribution function is evaluated.
+ * @return the probability that a random variable with this
+ * distribution takes a value less than or equal to <code>x</code>
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ double cumulativeProbability(double x) throws MathException;
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(x0 &le; X &le; x1).
+ *
+ * @param x0 the (inclusive) 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</code> and <code>x1</code>,
+ * including the endpoints
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>x0 > x1</code>
+ */
+ double cumulativeProbability(double x0, double x1) throws MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ExponentialDistribution.java b/src/main/java/org/apache/commons/math/distribution/ExponentialDistribution.java
new file mode 100644
index 0000000..6d3fbe2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ExponentialDistribution.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.math.distribution;
+
+/**
+ * The Exponential Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
+ * Exponential Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface ExponentialDistribution extends ContinuousDistribution, HasDensity<Double> {
+ /**
+ * Modify the mean.
+ * @param mean the new mean.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setMean(double mean);
+
+ /**
+ * Access the mean.
+ * @return the mean.
+ */
+ double getMean();
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ */
+ double density(Double x);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java
new file mode 100644
index 0000000..25d81f4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * The default implementation of {@link ExponentialDistribution}.
+ *
+ * @version $Revision: 1055914 $ $Date: 2011-01-06 16:34:34 +0100 (jeu. 06 janv. 2011) $
+ */
+public class ExponentialDistributionImpl extends AbstractContinuousDistribution
+ implements ExponentialDistribution, Serializable {
+
+ /**
+ * 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;
+
+ /** The mean of this distribution. */
+ private double mean;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a exponential distribution with the given mean.
+ * @param mean mean of this distribution.
+ */
+ public ExponentialDistributionImpl(double mean) {
+ this(mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a exponential distribution with the given mean.
+ * @param mean mean of this distribution.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
+ * @since 2.1
+ */
+ public ExponentialDistributionImpl(double mean, double inverseCumAccuracy) {
+ super();
+ setMeanInternal(mean);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Modify the mean.
+ * @param mean the new mean.
+ * @throws IllegalArgumentException if <code>mean</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setMean(double mean) {
+ setMeanInternal(mean);
+ }
+ /**
+ * Modify the mean.
+ * @param newMean the new mean.
+ * @throws IllegalArgumentException if <code>newMean</code> is not positive.
+ */
+ private void setMeanInternal(double newMean) {
+ if (newMean <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_MEAN, newMean);
+ }
+ this.mean = newMean;
+ }
+
+ /**
+ * Access the mean.
+ * @return the mean.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @deprecated - use density(double)
+ */
+ @Deprecated
+ public double density(Double x) {
+ return density(x.doubleValue());
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ if (x < 0) {
+ return 0;
+ }
+ return FastMath.exp(-x / mean) / mean;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; x).
+ *
+ * The implementation of this method is based on:
+ * <ul>
+ * <li>
+ * <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
+ * Exponential Distribution</a>, equation (1).</li>
+ * </ul>
+ *
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException{
+ double ret;
+ if (x <= 0.0) {
+ ret = 0.0;
+ } else {
+ ret = 1.0 - FastMath.exp(-x / mean);
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if p < 0 or p > 1.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) throws MathException {
+ double ret;
+
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ } else if (p == 1.0) {
+ ret = Double.POSITIVE_INFINITY;
+ } else {
+ ret = -mean * FastMath.log(1.0 - p);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Generates a random value sampled from this distribution.
+ *
+ * <p><strong>Algorithm Description</strong>: 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. </p>
+ *
+ * @return random value
+ * @since 2.2
+ * @throws MathException if an error occurs generating the random value
+ */
+ @Override
+ public double sample() throws MathException {
+ return randomData.nextExponential(mean);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return 0;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ // NOTE: exponential is skewed to the left
+ // NOTE: therefore, P(X < &mu;) > .5
+
+ if (p < .5) {
+ // use mean
+ return mean;
+ } else {
+ // use max
+ return Double.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ // TODO: try to improve on this estimate
+ // TODO: what should really happen here is not derive from AbstractContinuousDistribution
+ // TODO: because the inverse cumulative distribution is simple.
+ // Exponential is skewed to the left, therefore, P(X < &mu;) > .5
+ if (p < .5) {
+ // use 1/2 mean
+ return mean * .5;
+ } else {
+ // use mean
+ return mean;
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0, regardless of the mean.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity,
+ * regardless of the mean.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean of the distribution.
+ *
+ * For mean parameter <code>k</code>, the mean is
+ * <code>k</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ return getMean();
+ }
+
+ /**
+ * Returns the variance of the distribution.
+ *
+ * For mean parameter <code>k</code>, the variance is
+ * <code>k^2</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double m = getMean();
+ return m * m;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/FDistribution.java b/src/main/java/org/apache/commons/math/distribution/FDistribution.java
new file mode 100644
index 0000000..51c33bf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/FDistribution.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.math.distribution;
+
+/**
+ * F-Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/F-Distribution.html">
+ * F-Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface FDistribution extends ContinuousDistribution {
+ /**
+ * Modify the numerator degrees of freedom.
+ * @param degreesOfFreedom the new numerator degrees of freedom.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setNumeratorDegreesOfFreedom(double degreesOfFreedom);
+
+ /**
+ * Access the numerator degrees of freedom.
+ * @return the numerator degrees of freedom.
+ */
+ double getNumeratorDegreesOfFreedom();
+
+ /**
+ * Modify the denominator degrees of freedom.
+ * @param degreesOfFreedom the new denominator degrees of freedom.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setDenominatorDegreesOfFreedom(double degreesOfFreedom);
+
+ /**
+ * Access the denominator degrees of freedom.
+ * @return the denominator degrees of freedom.
+ */
+ double getDenominatorDegreesOfFreedom();
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/FDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/FDistributionImpl.java
new file mode 100644
index 0000000..5b4049e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/FDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Beta;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Default implementation of
+ * {@link org.apache.commons.math.distribution.FDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class FDistributionImpl
+ extends AbstractContinuousDistribution
+ implements FDistribution, Serializable {
+
+ /**
+ * 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 double numeratorDegreesOfFreedom;
+
+ /** The numerator degrees of freedom*/
+ private double denominatorDegreesOfFreedom;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a F distribution using the given degrees of freedom.
+ * @param numeratorDegreesOfFreedom the numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom the denominator degrees of freedom.
+ */
+ public FDistributionImpl(double numeratorDegreesOfFreedom,
+ double denominatorDegreesOfFreedom) {
+ this(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a F distribution using the given degrees of freedom and inverse cumulative probability accuracy.
+ * @param numeratorDegreesOfFreedom the numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom the denominator 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 FDistributionImpl(double numeratorDegreesOfFreedom, double denominatorDegreesOfFreedom,
+ double inverseCumAccuracy) {
+ super();
+ setNumeratorDegreesOfFreedomInternal(numeratorDegreesOfFreedom);
+ setDenominatorDegreesOfFreedomInternal(denominatorDegreesOfFreedom);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(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 FastMath.exp(nhalf*logn + nhalf*logx - logx + mhalf*logm - nhalf*lognxm -
+ mhalf*lognxm - Beta.logBeta(nhalf, mhalf));
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; x).
+ *
+ * The implementation of this method is based on:
+ * <ul>
+ * <li>
+ * <a href="http://mathworld.wolfram.com/F-Distribution.html">
+ * F-Distribution</a>, equation (4).</li>
+ * </ul>
+ *
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException {
+ double ret;
+ if (x <= 0.0) {
+ ret = 0.0;
+ } else {
+ double n = numeratorDegreesOfFreedom;
+ double m = denominatorDegreesOfFreedom;
+
+ ret = Beta.regularizedBeta((n * x) / (m + n * x),
+ 0.5 * n,
+ 0.5 * m);
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p == 0) {
+ return 0d;
+ }
+ if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return 0.0;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ return Double.MAX_VALUE;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ double ret = 1.0;
+ double d = denominatorDegreesOfFreedom;
+ if (d > 2.0) {
+ // use mean
+ ret = d / (d - 2.0);
+ }
+ return ret;
+ }
+
+ /**
+ * Modify the numerator degrees of freedom.
+ * @param degreesOfFreedom the new numerator degrees of freedom.
+ * @throws IllegalArgumentException if <code>degreesOfFreedom</code> is not
+ * positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNumeratorDegreesOfFreedom(double degreesOfFreedom) {
+ setNumeratorDegreesOfFreedomInternal(degreesOfFreedom);
+ }
+
+ /**
+ * Modify the numerator degrees of freedom.
+ * @param degreesOfFreedom the new numerator degrees of freedom.
+ * @throws IllegalArgumentException if <code>degreesOfFreedom</code> is not
+ * positive.
+ */
+ private void setNumeratorDegreesOfFreedomInternal(double degreesOfFreedom) {
+ if (degreesOfFreedom <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_DEGREES_OF_FREEDOM, degreesOfFreedom);
+ }
+ this.numeratorDegreesOfFreedom = degreesOfFreedom;
+ }
+
+ /**
+ * Access the numerator degrees of freedom.
+ * @return the numerator degrees of freedom.
+ */
+ public double getNumeratorDegreesOfFreedom() {
+ return numeratorDegreesOfFreedom;
+ }
+
+ /**
+ * Modify the denominator degrees of freedom.
+ * @param degreesOfFreedom the new denominator degrees of freedom.
+ * @throws IllegalArgumentException if <code>degreesOfFreedom</code> is not
+ * positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setDenominatorDegreesOfFreedom(double degreesOfFreedom) {
+ setDenominatorDegreesOfFreedomInternal(degreesOfFreedom);
+ }
+
+ /**
+ * Modify the denominator degrees of freedom.
+ * @param degreesOfFreedom the new denominator degrees of freedom.
+ * @throws IllegalArgumentException if <code>degreesOfFreedom</code> is not
+ * positive.
+ */
+ private void setDenominatorDegreesOfFreedomInternal(double degreesOfFreedom) {
+ if (degreesOfFreedom <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_DEGREES_OF_FREEDOM, degreesOfFreedom);
+ }
+ this.denominatorDegreesOfFreedom = degreesOfFreedom;
+ }
+
+ /**
+ * Access the denominator degrees of freedom.
+ * @return the denominator degrees of freedom.
+ */
+ public double getDenominatorDegreesOfFreedom() {
+ return denominatorDegreesOfFreedom;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0, regardless of the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity,
+ * regardless of the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean of the distribution.
+ *
+ * For denominator degrees of freedom parameter <code>b</code>,
+ * the mean is
+ * <ul>
+ * <li>if <code>b &gt; 2</code> then <code>b / (b - 2)</code></li>
+ * <li>else <code>undefined</code>
+ * </ul>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ final double denominatorDF = getDenominatorDegreesOfFreedom();
+
+ if (denominatorDF > 2) {
+ return denominatorDF / (denominatorDF - 2);
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the variance of the distribution.
+ *
+ * For numerator degrees of freedom parameter <code>a</code>
+ * and denominator degrees of freedom parameter <code>b</code>,
+ * the variance is
+ * <ul>
+ * <li>
+ * if <code>b &gt; 4</code> then
+ * <code>[ 2 * b^2 * (a + b - 2) ] / [ a * (b - 2)^2 * (b - 4) ]</code>
+ * </li>
+ * <li>else <code>undefined</code>
+ * </ul>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ 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;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/GammaDistribution.java b/src/main/java/org/apache/commons/math/distribution/GammaDistribution.java
new file mode 100644
index 0000000..71f8f78
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/GammaDistribution.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.math.distribution;
+
+/**
+ * The Gamma Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/GammaDistribution.html">
+ * Gamma Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface GammaDistribution extends ContinuousDistribution, HasDensity<Double> {
+ /**
+ * Modify the shape parameter, alpha.
+ * @param alpha the new shape parameter.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setAlpha(double alpha);
+
+ /**
+ * Access the shape parameter, alpha
+ * @return alpha.
+ */
+ double getAlpha();
+
+ /**
+ * Modify the scale parameter, beta.
+ * @param beta the new scale parameter.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setBeta(double beta);
+
+ /**
+ * Access the scale parameter, beta
+ * @return beta.
+ */
+ double getBeta();
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ */
+ double density(Double x);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/GammaDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/GammaDistributionImpl.java
new file mode 100644
index 0000000..a187892
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/GammaDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * The default implementation of {@link GammaDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class GammaDistributionImpl extends AbstractContinuousDistribution
+ implements GammaDistribution, Serializable {
+
+ /**
+ * 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 = -3239549463135430361L;
+
+ /** The shape parameter. */
+ private double alpha;
+
+ /** The scale parameter. */
+ private double beta;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a new gamma distribution with the given alpha and beta values.
+ * @param alpha the shape parameter.
+ * @param beta the scale parameter.
+ */
+ public GammaDistributionImpl(double alpha, double beta) {
+ this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a new gamma distribution with the given alpha and beta values.
+ * @param alpha the shape parameter.
+ * @param beta the scale parameter.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
+ * @since 2.1
+ */
+ public GammaDistributionImpl(double alpha, double beta, double inverseCumAccuracy) {
+ super();
+ setAlphaInternal(alpha);
+ setBetaInternal(beta);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; x).
+ *
+ * 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>
+ * <li>Casella, G., & Berger, R. (1990). <i>Statistical Inference</i>.
+ * Belmont, CA: Duxbury Press.</li>
+ * </ul>
+ *
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException{
+ double ret;
+
+ if (x <= 0.0) {
+ ret = 0.0;
+ } else {
+ ret = Gamma.regularizedGammaP(alpha, x / beta);
+ }
+
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p == 0) {
+ return 0d;
+ }
+ if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Modify the shape parameter, alpha.
+ * @param alpha the new shape parameter.
+ * @throws IllegalArgumentException if <code>alpha</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setAlpha(double alpha) {
+ setAlphaInternal(alpha);
+ }
+
+ /**
+ * Modify the shape parameter, alpha.
+ * @param newAlpha the new shape parameter.
+ * @throws IllegalArgumentException if <code>newAlpha</code> is not positive.
+ */
+ private void setAlphaInternal(double newAlpha) {
+ if (newAlpha <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_ALPHA,
+ newAlpha);
+ }
+ this.alpha = newAlpha;
+ }
+
+ /**
+ * Access the shape parameter, alpha
+ * @return alpha.
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+ /**
+ * Modify the scale parameter, beta.
+ * @param newBeta the new scale parameter.
+ * @throws IllegalArgumentException if <code>newBeta</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setBeta(double newBeta) {
+ setBetaInternal(newBeta);
+ }
+
+ /**
+ * Modify the scale parameter, beta.
+ * @param newBeta the new scale parameter.
+ * @throws IllegalArgumentException if <code>newBeta</code> is not positive.
+ */
+ private void setBetaInternal(double newBeta) {
+ if (newBeta <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_BETA,
+ newBeta);
+ }
+ this.beta = newBeta;
+ }
+
+ /**
+ * Access the scale parameter, beta
+ * @return beta.
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ */
+ @Override
+ public double density(double x) {
+ if (x < 0) return 0;
+ return FastMath.pow(x / beta, alpha - 1) / beta * FastMath.exp(-x / beta) / FastMath.exp(Gamma.logGamma(alpha));
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @deprecated
+ */
+ @Deprecated
+ public double density(Double x) {
+ return density(x.doubleValue());
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ // TODO: try to improve on this estimate
+ return Double.MIN_VALUE;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ // TODO: try to improve on this estimate
+ // NOTE: gamma is skewed to the left
+ // NOTE: therefore, P(X < &mu;) > .5
+
+ double ret;
+
+ if (p < .5) {
+ // use mean
+ ret = alpha * beta;
+ } else {
+ // use max value
+ ret = Double.MAX_VALUE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ // TODO: try to improve on this estimate
+ // Gamma is skewed to the left, therefore, P(X < &mu;) > .5
+
+ double ret;
+
+ if (p < .5) {
+ // use 1/2 mean
+ ret = alpha * beta * .5;
+ } else {
+ // use mean
+ ret = alpha * beta;
+ }
+
+ 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;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0, regardless of the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity,
+ * regardless of the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For shape parameter <code>alpha</code> and scale
+ * parameter <code>beta</code>, the mean is
+ * <code>alpha * beta</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ return getAlpha() * getBeta();
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For shape parameter <code>alpha</code> and scale
+ * parameter <code>beta</code>, the variance is
+ * <code>alpha * beta^2</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double b = getBeta();
+ return getAlpha() * b * b;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/HasDensity.java b/src/main/java/org/apache/commons/math/distribution/HasDensity.java
new file mode 100644
index 0000000..0e15c6c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/HasDensity.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * <p>Interface that signals that a distribution can compute the probability density function
+ * for a particular point.
+ * @param <P> the type of the point at which density is to be computed, this
+ * may be for example <code>Double.</code></p>
+ *
+ * <p>This interface is deprecated. As of version 2.0, the {@link ContinuousDistribution}
+ * interface will be extended to include a <code>density(double)<code> method.</p>
+ *
+ * @deprecated to be removed in math 3.0
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ */
+@Deprecated
+public interface HasDensity<P> {
+
+ /**
+ * Compute the probability density function.
+ * @param x point for which the probability density is requested
+ * @return probability density at point x
+ * @throws MathException if probability density cannot be computed at specifed point
+ */
+ double density(P x) throws MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/HypergeometricDistribution.java b/src/main/java/org/apache/commons/math/distribution/HypergeometricDistribution.java
new file mode 100644
index 0000000..d3595e0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/HypergeometricDistribution.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.math.distribution;
+
+/**
+ * The Hypergeometric Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/HypergeometricDistribution.html">
+ * Hypergeometric Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface HypergeometricDistribution extends IntegerDistribution {
+
+ /**
+ * Access the number of successes.
+ * @return the number of successes.
+ */
+ int getNumberOfSuccesses();
+
+ /**
+ * Access the population size.
+ * @return the population size.
+ */
+ int getPopulationSize();
+
+ /**
+ * Access the sample size.
+ * @return the sample size.
+ */
+ int getSampleSize();
+
+ /**
+ * Modify the number of successes.
+ * @param num the new number of successes.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setNumberOfSuccesses(int num);
+
+ /**
+ * Modify the population size.
+ * @param size the new population size.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setPopulationSize(int size);
+
+ /**
+ * Modify the sample size.
+ * @param size the new sample size.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setSampleSize(int size);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/HypergeometricDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/HypergeometricDistributionImpl.java
new file mode 100644
index 0000000..f9dff2d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/HypergeometricDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * The default implementation of {@link HypergeometricDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class HypergeometricDistributionImpl extends AbstractIntegerDistribution
+ implements HypergeometricDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -436928820673516179L;
+
+ /** The number of successes in the population. */
+ private int numberOfSuccesses;
+
+ /** The population size. */
+ private int populationSize;
+
+ /** The sample size. */
+ private int sampleSize;
+
+ /**
+ * Construct a new hypergeometric distribution with the given the population
+ * size, the number of successes in the population, and the sample size.
+ *
+ * @param populationSize the population size.
+ * @param numberOfSuccesses number of successes in the population.
+ * @param sampleSize the sample size.
+ */
+ public HypergeometricDistributionImpl(int populationSize,
+ int numberOfSuccesses, int sampleSize) {
+ super();
+ if (numberOfSuccesses > populationSize) {
+ throw MathRuntimeException
+ .createIllegalArgumentException(
+ LocalizedFormats.NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE,
+ numberOfSuccesses, populationSize);
+ }
+ if (sampleSize > populationSize) {
+ throw MathRuntimeException
+ .createIllegalArgumentException(
+ LocalizedFormats.SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE,
+ sampleSize, populationSize);
+ }
+
+ setPopulationSizeInternal(populationSize);
+ setSampleSizeInternal(sampleSize);
+ setNumberOfSuccessesInternal(numberOfSuccesses);
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &le; x).
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return PDF for this distribution.
+ */
+ @Override
+ 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, populationSize,
+ numberOfSuccesses, sampleSize);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Return the domain for the given hypergeometric distribution parameters.
+ *
+ * @param n the population size.
+ * @param m number of successes in the population.
+ * @param k the 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) };
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e. P(X &lt; <i>lower bound</i>) &lt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainLowerBound(double p) {
+ return getLowerDomain(populationSize, numberOfSuccesses, sampleSize);
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e. P(X &lt; <i>upper bound</i>) &gt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainUpperBound(double p) {
+ return getUpperDomain(sampleSize, numberOfSuccesses);
+ }
+
+ /**
+ * Return the lowest domain value for the given hypergeometric distribution
+ * parameters.
+ *
+ * @param n the population size.
+ * @param m number of successes in the population.
+ * @param k the 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 the sample size.
+ * @return the highest domain value of the hypergeometric distribution.
+ */
+ private int getUpperDomain(int m, int k) {
+ return FastMath.min(k, m);
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X = x).
+ *
+ * @param x the value at which the PMF is evaluated.
+ * @return PMF for this distribution.
+ */
+ public double probability(int x) {
+ double ret;
+
+ int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
+ if (x < domain[0] || x > domain[1]) {
+ ret = 0.0;
+ } 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 = FastMath.exp(p1 + p2 - p3);
+ }
+
+ return ret;
+ }
+
+ /**
+ * For the distribution, X, defined by the given hypergeometric distribution
+ * parameters, this method returns P(X = x).
+ *
+ * @param n the population size.
+ * @param m number of successes in the population.
+ * @param k the sample size.
+ * @param x the value at which the PMF is evaluated.
+ * @return PMF for the distribution.
+ */
+ private double probability(int n, int m, int k, int x) {
+ return FastMath.exp(MathUtils.binomialCoefficientLog(m, x) +
+ MathUtils.binomialCoefficientLog(n - m, k - x) -
+ MathUtils.binomialCoefficientLog(n, k));
+ }
+
+ /**
+ * Modify the number of successes.
+ *
+ * @param num the new number of successes.
+ * @throws IllegalArgumentException if <code>num</code> is negative.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNumberOfSuccesses(int num) {
+ setNumberOfSuccessesInternal(num);
+ }
+
+ /**
+ * Modify the number of successes.
+ *
+ * @param num the new number of successes.
+ * @throws IllegalArgumentException if <code>num</code> is negative.
+ */
+ private void setNumberOfSuccessesInternal(int num) {
+ if (num < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_NUMBER_OF_SUCCESSES, num);
+ }
+ numberOfSuccesses = num;
+ }
+
+ /**
+ * Modify the population size.
+ *
+ * @param size the new population size.
+ * @throws IllegalArgumentException if <code>size</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setPopulationSize(int size) {
+ setPopulationSizeInternal(size);
+ }
+
+ /**
+ * Modify the population size.
+ *
+ * @param size the new population size.
+ * @throws IllegalArgumentException if <code>size</code> is not positive.
+ */
+ private void setPopulationSizeInternal(int size) {
+ if (size <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_POPULATION_SIZE, size);
+ }
+ populationSize = size;
+ }
+
+ /**
+ * Modify the sample size.
+ *
+ * @param size the new sample size.
+ * @throws IllegalArgumentException if <code>size</code> is negative.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setSampleSize(int size) {
+ setSampleSizeInternal(size);
+ }
+ /**
+ * Modify the sample size.
+ *
+ * @param size the new sample size.
+ * @throws IllegalArgumentException if <code>size</code> is negative.
+ */
+ private void setSampleSizeInternal(int size) {
+ if (size < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_SAMPLE_SIZE, size);
+ }
+ sampleSize = size;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &ge; x).
+ *
+ * @param x the value at which the CDF is evaluated.
+ * @return 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, populationSize, numberOfSuccesses, sampleSize);
+ }
+
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(x0 &le; X &le; x1). This
+ * probability is computed by summing the point probabilities for the values
+ * x0, x0 + 1, x0 + 2, ..., x1, in the order directed by dx.
+ *
+ * @param x0 the inclusive, lower bound
+ * @param x1 the inclusive, upper bound
+ * @param dx the direction of summation. 1 indicates summing from x0 to x1.
+ * 0 indicates summing from x1 to x0.
+ * @param n the population size.
+ * @param m number of successes in the population.
+ * @param k the sample size.
+ * @return P(x0 &le; X &le; x1).
+ */
+ private double innerCumulativeProbability(int x0, int x1, int dx, int n,
+ int m, int k) {
+ double ret = probability(n, m, k, x0);
+ while (x0 != x1) {
+ x0 += dx;
+ ret += probability(n, m, k, x0);
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the lower bound for the support for the distribution.
+ *
+ * For population size <code>N</code>,
+ * number of successes <code>m</code>, and
+ * sample size <code>n</code>,
+ * the lower bound of the support is
+ * <code>max(0, n + m - N)</code>
+ *
+ * @return lower bound of the support
+ * @since 2.2
+ */
+ public int getSupportLowerBound() {
+ return FastMath.max(0,
+ getSampleSize() + getNumberOfSuccesses() - getPopulationSize());
+ }
+
+ /**
+ * Returns the upper bound for the support of the distribution.
+ *
+ * For number of successes <code>m</code> and
+ * sample size <code>n</code>,
+ * the upper bound of the support is
+ * <code>min(m, n)</code>
+ *
+ * @return upper bound of the support
+ * @since 2.2
+ */
+ public int getSupportUpperBound() {
+ return FastMath.min(getNumberOfSuccesses(), getSampleSize());
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For population size <code>N</code>,
+ * number of successes <code>m</code>, and
+ * sample size <code>n</code>, the mean is
+ * <code>n * m / N</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ protected double getNumericalMean() {
+ return (double)(getSampleSize() * getNumberOfSuccesses()) / (double)getPopulationSize();
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For population size <code>N</code>,
+ * number of successes <code>m</code>, and
+ * sample size <code>n</code>, the variance is
+ * <code>[ n * m * (N - n) * (N - m) ] / [ N^2 * (N - 1) ]</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double N = getPopulationSize();
+ final double m = getNumberOfSuccesses();
+ final double n = getSampleSize();
+ return ( n * m * (N - n) * (N - m) ) / ( (N*N * (N - 1)) );
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/IntegerDistribution.java b/src/main/java/org/apache/commons/math/distribution/IntegerDistribution.java
new file mode 100644
index 0000000..096af17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/IntegerDistribution.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * Interface for discrete distributions of integer-valued random variables.
+ *
+ * @version $Revision: 949535 $ $Date: 2010-05-30 19:00:15 +0200 (dim. 30 mai 2010) $
+ */
+public interface IntegerDistribution extends DiscreteDistribution {
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X = x). In other words, this
+ * method represents the probability mass function for the distribution.
+ *
+ * @param x the value at which the probability density function is evaluated.
+ * @return the value of the probability density function at x
+ */
+ double probability(int x);
+
+ /**
+ * For a random variable X whose values are distributed according
+ * to this distribution, this method returns P(X &le; x). In other words,
+ * this method represents the probability distribution function, or PDF
+ * for the distribution.
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return PDF for this distribution.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ double cumulativeProbability(int x) throws MathException;
+
+ /**
+ * For this distribution, X, this method returns P(x0 &le; X &le; x1).
+ * @param x0 the inclusive, lower bound
+ * @param x1 the inclusive, upper bound
+ * @return the cumulative probability.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if x0 > x1
+ */
+ double cumulativeProbability(int x0, int x1) throws MathException;
+
+ /**
+ * For this distribution, X, this method returns the largest x such that
+ * P(X &le; x) <= p.
+ * <p>
+ * Note that this definition implies: <ul>
+ * <li> If there is a minimum value, <code>m</code>, with positive
+ * probability under (the density of) X, then <code>m - 1</code> is
+ * returned by <code>inverseCumulativeProbability(0).</code> If there is
+ * no such value <code>m, Integer.MIN_VALUE</code> is
+ * returned.</li>
+ * <li> If there is a maximum value, <code>M</code>, such that
+ * P(X &le; M) =1, then <code>M</code> is returned by
+ * <code>inverseCumulativeProbability(1).</code>
+ * If there is no such value, <code>M, Integer.MAX_VALUE</code> is
+ * returned.</li></ul></p>
+ *
+ * @param p the cumulative probability.
+ * @return the largest x such that P(X &le; x) <= p
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if p is not between 0 and 1 (inclusive)
+ */
+ int inverseCumulativeProbability(double p) throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/NormalDistribution.java b/src/main/java/org/apache/commons/math/distribution/NormalDistribution.java
new file mode 100644
index 0000000..67f5d5c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/NormalDistribution.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.math.distribution;
+
+/**
+ * Normal (Gauss) Distribution.
+ *
+ * <p>
+ * References:</p><p>
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/NormalDistribution.html">
+ * Normal Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface NormalDistribution extends ContinuousDistribution, HasDensity<Double> {
+ /**
+ * Access the mean.
+ * @return mean for this distribution
+ */
+ double getMean();
+ /**
+ * Modify the mean.
+ * @param mean for this distribution
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setMean(double mean);
+ /**
+ * Access the standard deviation.
+ * @return standard deviation for this distribution
+ */
+ double getStandardDeviation();
+ /**
+ * Modify the standard deviation.
+ * @param sd standard deviation for this distribution
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setStandardDeviation(double sd);
+
+ /**
+ * Return the probability density for a particular point.
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ */
+ double density(Double x);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java
new file mode 100644
index 0000000..1164649
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Erf;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Default implementation of
+ * {@link org.apache.commons.math.distribution.NormalDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class NormalDistributionImpl extends AbstractContinuousDistribution
+ implements NormalDistribution, Serializable {
+
+ /**
+ * 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;
+
+ /** &sqrt;(2 &pi;) */
+ private static final double SQRT2PI = FastMath.sqrt(2 * FastMath.PI);
+
+ /** The mean of this distribution. */
+ private double mean = 0;
+
+ /** The standard deviation of this distribution. */
+ private double standardDeviation = 1;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a normal distribution using the given mean and standard deviation.
+ * @param mean mean for this distribution
+ * @param sd standard deviation for this distribution
+ */
+ public NormalDistributionImpl(double mean, double sd){
+ this(mean, sd, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a normal distribution using the given mean, standard deviation and
+ * inverse cumulative distribution accuracy.
+ *
+ * @param mean mean for this distribution
+ * @param sd standard deviation for this distribution
+ * @param inverseCumAccuracy inverse cumulative probability accuracy
+ * @since 2.1
+ */
+ public NormalDistributionImpl(double mean, double sd, double inverseCumAccuracy) {
+ super();
+ setMeanInternal(mean);
+ setStandardDeviationInternal(sd);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Creates normal distribution with the mean equal to zero and standard
+ * deviation equal to one.
+ */
+ public NormalDistributionImpl(){
+ this(0.0, 1.0);
+ }
+
+ /**
+ * Access the mean.
+ * @return mean for this distribution
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /**
+ * Modify the mean.
+ * @param mean for this distribution
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setMean(double mean) {
+ setMeanInternal(mean);
+ }
+
+ /**
+ * Modify the mean.
+ * @param newMean for this distribution
+ */
+ private void setMeanInternal(double newMean) {
+ this.mean = newMean;
+ }
+
+ /**
+ * Access the standard deviation.
+ * @return standard deviation for this distribution
+ */
+ public double getStandardDeviation() {
+ return standardDeviation;
+ }
+
+ /**
+ * Modify the standard deviation.
+ * @param sd standard deviation for this distribution
+ * @throws IllegalArgumentException if <code>sd</code> is not positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setStandardDeviation(double sd) {
+ setStandardDeviationInternal(sd);
+ }
+
+ /**
+ * Modify the standard deviation.
+ * @param sd standard deviation for this distribution
+ * @throws IllegalArgumentException if <code>sd</code> is not positive.
+ */
+ private void setStandardDeviationInternal(double sd) {
+ if (sd <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_STANDARD_DEVIATION,
+ sd);
+ }
+ standardDeviation = sd;
+ }
+
+ /**
+ * Return the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @deprecated
+ */
+ @Deprecated
+ public double density(Double x) {
+ return density(x.doubleValue());
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ double x0 = x - mean;
+ return FastMath.exp(-x0 * x0 / (2 * standardDeviation * standardDeviation)) / (standardDeviation * SQRT2PI);
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; <code>x</code>).
+ * If <code>x</code>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</code> of 0 or 1.
+ *
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF evaluated at <code>x</code>.
+ * @throws MathException if the algorithm fails to converge
+ */
+ public double cumulativeProbability(double x) throws MathException {
+ final double dev = x - mean;
+ if (FastMath.abs(dev) > 40 * standardDeviation) {
+ return dev < 0 ? 0.0d : 1.0d;
+ }
+ return 0.5 * (1.0 + Erf.erf(dev /
+ (standardDeviation * FastMath.sqrt(2.0))));
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and
+ * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p == 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Generates a random value sampled from this distribution.
+ *
+ * @return random value
+ * @since 2.2
+ * @throws MathException if an error occurs generating the random value
+ */
+ @Override
+ public double sample() throws MathException {
+ return randomData.nextGaussian(mean, standardDeviation);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = -Double.MAX_VALUE;
+ } else {
+ ret = mean;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = mean;
+ } else {
+ ret = Double.MAX_VALUE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ double ret;
+
+ if (p < .5) {
+ ret = mean - standardDeviation;
+ } else if (p > .5) {
+ ret = mean + standardDeviation;
+ } else {
+ ret = mean;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always negative infinity
+ * no matter the parameters.
+ *
+ * @return lower bound of the support (always Double.NEGATIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity
+ * no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For standard deviation parameter <code>s</code>,
+ * the variance is <code>s^2</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double s = getStandardDeviation();
+ return s * s;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/PascalDistribution.java b/src/main/java/org/apache/commons/math/distribution/PascalDistribution.java
new file mode 100644
index 0000000..88c5924
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/PascalDistribution.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.math.distribution;
+
+/**
+ * The Pascal distribution. The Pascal distribution is a special case of the
+ * Negative Binomial distribution where the number of successes parameter is an
+ * integer.
+ *
+ * There are various ways to express the probability mass and distribution
+ * functions for the Pascal distribution. The convention employed by the
+ * library is to express these functions in terms of the number of failures in
+ * a Bernoulli experiment [2].
+ *
+ * <p>
+ * References:
+ * <ol>
+ * <li><a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">
+ * Negative Binomial Distribution</a></li>
+ * <oi><a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution#Waiting_time_in_a_Bernoulli_process">Waiting Time in a Bernoulli Process</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ * @since 1.2
+ */
+public interface PascalDistribution extends IntegerDistribution {
+ /**
+ * Access the number of successes for this distribution.
+ *
+ * @return the number of successes
+ */
+ int getNumberOfSuccesses();
+
+ /**
+ * Access the probability of success for this distribution.
+ *
+ * @return the probability of success
+ */
+ double getProbabilityOfSuccess();
+
+ /**
+ * Change the number of successes for this distribution.
+ *
+ * @param successes the new number of successes
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setNumberOfSuccesses(int successes);
+
+ /**
+ * Change the probability of success for this distribution.
+ *
+ * @param p the new probability of success
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setProbabilityOfSuccess(double p);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/PascalDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/PascalDistributionImpl.java
new file mode 100644
index 0000000..437cbc7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/PascalDistributionImpl.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Beta;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * The default implementation of {@link PascalDistribution}.
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ * @since 1.2
+ */
+public class PascalDistributionImpl extends AbstractIntegerDistribution
+ implements PascalDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 6751309484392813623L;
+
+ /** The number of successes */
+ private int numberOfSuccesses;
+
+ /** The probability of success */
+ private double probabilityOfSuccess;
+
+ /**
+ * Create a Pascal distribution with the given number of trials and
+ * probability of success.
+ * @param r the number of successes
+ * @param p the probability of success
+ */
+ public PascalDistributionImpl(int r, double p) {
+ super();
+ setNumberOfSuccessesInternal(r);
+ setProbabilityOfSuccessInternal(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;
+ }
+
+ /**
+ * Change the number of successes for this distribution.
+ * @param successes the new number of successes
+ * @throws IllegalArgumentException if <code>successes</code> is not
+ * positive.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNumberOfSuccesses(int successes) {
+ setNumberOfSuccessesInternal(successes);
+ }
+
+ /**
+ * Change the number of successes for this distribution.
+ * @param successes the new number of successes
+ * @throws IllegalArgumentException if <code>successes</code> is not
+ * positive.
+ */
+ private void setNumberOfSuccessesInternal(int successes) {
+ if (successes < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_NUMBER_OF_SUCCESSES,
+ successes);
+ }
+ numberOfSuccesses = successes;
+ }
+
+ /**
+ * Change the probability of success for this distribution.
+ * @param p the new probability of success
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setProbabilityOfSuccess(double p) {
+ setProbabilityOfSuccessInternal(p);
+ }
+
+ /**
+ * Change the probability of success for this distribution.
+ * @param p the new probability of success
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ private void setProbabilityOfSuccessInternal(double p) {
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, p, 0.0, 1.0);
+ }
+ probabilityOfSuccess = p;
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e. P(X &lt; <i>lower bound</i>) &lt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainLowerBound(double p) {
+ return -1;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e. P(X &lt; <i>upper bound</i>) &gt;
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainUpperBound(double p) {
+ // use MAX - 1 because MAX causes loop
+ return Integer.MAX_VALUE - 1;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &le; x).
+ * @param x the value at which the PDF is evaluated
+ * @return PDF for this distribution
+ * @throws MathException if the cumulative probability can not be computed
+ * due to convergence or other numerical errors
+ */
+ @Override
+ public double cumulativeProbability(int x) throws MathException {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else {
+ ret = Beta.regularizedBeta(probabilityOfSuccess,
+ numberOfSuccesses, x + 1);
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X = x).
+ * @param x the value at which the PMF is evaluated
+ * @return PMF for this distribution
+ */
+ public double probability(int x) {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else {
+ ret = MathUtils.binomialCoefficientDouble(x +
+ numberOfSuccesses - 1, numberOfSuccesses - 1) *
+ FastMath.pow(probabilityOfSuccess, numberOfSuccesses) *
+ FastMath.pow(1.0 - probabilityOfSuccess, x);
+ }
+ return ret;
+ }
+
+ /**
+ * For this distribution, X, this method returns the largest x, such that
+ * P(X &le; x) &le; <code>p</code>.
+ * <p>
+ * Returns <code>-1</code> for p=0 and <code>Integer.MAX_VALUE</code>
+ * for p=1.</p>
+ * @param p the desired probability
+ * @return the largest x such that P(X &le; x) <= p
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if p < 0 or p > 1
+ */
+ @Override
+ public int inverseCumulativeProbability(final double p)
+ throws MathException {
+ int ret;
+
+ // handle extreme values explicitly
+ if (p == 0) {
+ ret = -1;
+ } else if (p == 1) {
+ ret = Integer.MAX_VALUE;
+ } else {
+ ret = super.inverseCumulativeProbability(p);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity
+ * no matter the parameters. Positive infinity is represented
+ * by <code>Integer.MAX_VALUE</code> together with
+ * {@link #isSupportUpperBoundInclusive()} being <code>false</code>
+ *
+ * @return upper bound of the support (always <code>Integer.MAX_VALUE</code> for positive infinity)
+ * @since 2.2
+ */
+ public int getSupportUpperBound() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For number of successes <code>r</code> and
+ * probability of success <code>p</code>, the mean is
+ * <code>( r * p ) / ( 1 - p )</code>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ final double p = getProbabilityOfSuccess();
+ final double r = getNumberOfSuccesses();
+ return ( r * p ) / ( 1 - p );
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For number of successes <code>r</code> and
+ * probability of success <code>p</code>, the mean is
+ * <code>( r * p ) / ( 1 - p )^2</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ final double p = getProbabilityOfSuccess();
+ final double r = getNumberOfSuccesses();
+ final double pInv = 1 - p;
+ return ( r * p ) / (pInv * pInv);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/PoissonDistribution.java b/src/main/java/org/apache/commons/math/distribution/PoissonDistribution.java
new file mode 100644
index 0000000..b3a2f12
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/PoissonDistribution.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.math.distribution;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * Interface representing the Poisson Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/PoissonDistribution.html">
+ * Poisson distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface PoissonDistribution extends IntegerDistribution {
+
+ /**
+ * Get the mean for the distribution.
+ *
+ * @return the mean for the distribution.
+ */
+ double getMean();
+
+ /**
+ * Set the mean for the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgument</code> is thrown.
+ *
+ * @param p the mean
+ * @throws IllegalArgumentException if p &le; 0
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setMean(double p);
+
+ /**
+ * Calculates the Poisson distribution function using a normal approximation.
+ *
+ * @param x the upper bound, inclusive
+ * @return the distribution function value calculated using a normal approximation
+ * @throws MathException if an error occurs computing the normal approximation
+ */
+ double normalApproximateProbability(int x) throws MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java
new file mode 100644
index 0000000..85623d4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implementation for the {@link PoissonDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class PoissonDistributionImpl extends AbstractIntegerDistribution
+ implements PoissonDistribution, Serializable {
+
+ /**
+ * 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 NormalDistribution normal;
+
+ /**
+ * Holds the Poisson mean for the distribution.
+ */
+ private double mean;
+
+ /**
+ * Maximum number of iterations for cumulative probability.
+ *
+ * Cumulative probabilities are estimated using either Lanczos series approximation of
+ * Gamma#regularizedGammaP or continued fraction approximation of Gamma#regularizedGammaQ.
+ */
+ private int maxIterations = DEFAULT_MAX_ITERATIONS;
+
+ /**
+ * Convergence criterion for cumulative probability.
+ */
+ private double epsilon = DEFAULT_EPSILON;
+
+ /**
+ * Create a new Poisson distribution with the given the mean. The mean value
+ * must be positive; otherwise an <code>IllegalArgument</code> is thrown.
+ *
+ * @param p the Poisson mean
+ * @throws IllegalArgumentException if p &le; 0
+ */
+ public PoissonDistributionImpl(double p) {
+ this(p, new NormalDistributionImpl());
+ }
+
+ /**
+ * Create a new Poisson distribution with the given mean, convergence criterion
+ * and maximum number of iterations.
+ *
+ * @param p the Poisson mean
+ * @param epsilon the convergence criteria for cumulative probabilites
+ * @param maxIterations the maximum number of iterations for cumulative probabilites
+ * @since 2.1
+ */
+ public PoissonDistributionImpl(double p, double epsilon, int maxIterations) {
+ setMean(p);
+ this.epsilon = epsilon;
+ this.maxIterations = maxIterations;
+ }
+
+ /**
+ * Create a new Poisson distribution with the given mean and convergence criterion.
+ *
+ * @param p the Poisson mean
+ * @param epsilon the convergence criteria for cumulative probabilites
+ * @since 2.1
+ */
+ public PoissonDistributionImpl(double p, double epsilon) {
+ setMean(p);
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Create a new Poisson distribution with the given mean and maximum number of iterations.
+ *
+ * @param p the Poisson mean
+ * @param maxIterations the maximum number of iterations for cumulative probabilites
+ * @since 2.1
+ */
+ public PoissonDistributionImpl(double p, int maxIterations) {
+ setMean(p);
+ this.maxIterations = maxIterations;
+ }
+
+
+ /**
+ * Create a new Poisson distribution with the given the mean. The mean value
+ * must be positive; otherwise an <code>IllegalArgument</code> is thrown.
+ *
+ * @param p the Poisson mean
+ * @param z a normal distribution used to compute normal approximations.
+ * @throws IllegalArgumentException if p &le; 0
+ * @since 1.2
+ * @deprecated as of 2.1 (to avoid possibly inconsistent state, the
+ * "NormalDistribution" will be instantiated internally)
+ */
+ @Deprecated
+ public PoissonDistributionImpl(double p, NormalDistribution z) {
+ super();
+ setNormalAndMeanInternal(z, p);
+ }
+
+ /**
+ * Get the Poisson mean for the distribution.
+ *
+ * @return the Poisson mean for the distribution.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /**
+ * Set the Poisson mean for the distribution. The mean value must be
+ * positive; otherwise an <code>IllegalArgument</code> is thrown.
+ *
+ * @param p the Poisson mean value
+ * @throws IllegalArgumentException if p &le; 0
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setMean(double p) {
+ setNormalAndMeanInternal(normal, p);
+ }
+ /**
+ * Set the Poisson mean for the distribution. The mean value must be
+ * positive; otherwise an <code>IllegalArgument</code> is thrown.
+ *
+ * @param z the new distribution
+ * @param p the Poisson mean value
+ * @throws IllegalArgumentException if p &le; 0
+ */
+ private void setNormalAndMeanInternal(NormalDistribution z,
+ double p) {
+ if (p <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_POISSON_MEAN, p);
+ }
+ mean = p;
+ normal = z;
+ normal.setMean(p);
+ normal.setStandardDeviation(FastMath.sqrt(p));
+ }
+
+ /**
+ * The probability mass function P(X = x) for a Poisson distribution.
+ *
+ * @param x the value at which the probability density function is
+ * evaluated.
+ * @return the value of the probability mass function at x
+ */
+ public double probability(int x) {
+ double ret;
+ if (x < 0 || x == Integer.MAX_VALUE) {
+ ret = 0.0;
+ } else if (x == 0) {
+ ret = FastMath.exp(-mean);
+ } else {
+ ret = FastMath.exp(-SaddlePointExpansion.getStirlingError(x) -
+ SaddlePointExpansion.getDeviancePart(x, mean)) /
+ FastMath.sqrt(MathUtils.TWO_PI * x);
+ }
+ return ret;
+ }
+
+ /**
+ * The probability distribution function P(X <= x) for a Poisson
+ * distribution.
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return Poisson distribution function evaluated at x
+ * @throws MathException if the cumulative probability can not be computed
+ * due to convergence or other numerical errors.
+ */
+ @Override
+ public double cumulativeProbability(int x) throws MathException {
+ 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))</code> distribution is used
+ * to approximate the Poisson distribution.
+ * <p>
+ * The computation uses "half-correction" -- evaluating the normal
+ * distribution function at <code>x + 0.5</code>
+ * </p>
+ *
+ * @param x the upper bound, inclusive
+ * @return the distribution function value calculated using a normal
+ * approximation
+ * @throws MathException if an error occurs computing the normal
+ * approximation
+ */
+ public double normalApproximateProbability(int x) throws MathException {
+ // calculate the probability using half-correction
+ return normal.cumulativeProbability(x + 0.5);
+ }
+
+ /**
+ * Generates a random value sampled from this distribution.
+ *
+ * <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><
+ *
+ * <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.</li></ul></p>
+ *
+ * @return random value
+ * @since 2.2
+ * @throws MathException if an error occurs generating the random value
+ */
+ @Override
+ public int sample() throws MathException {
+ return (int) FastMath.min(randomData.nextPoisson(mean), Integer.MAX_VALUE);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain lower bound
+ */
+ @Override
+ protected int getDomainLowerBound(double p) {
+ return 0;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain upper bound
+ */
+ @Override
+ protected int getDomainUpperBound(double p) {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Modify the normal distribution used to compute normal approximations. The
+ * caller is responsible for insuring the normal distribution has the proper
+ * parameter settings.
+ *
+ * @param value the new distribution
+ * @since 1.2
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNormal(NormalDistribution value) {
+ setNormalAndMeanInternal(value, mean);
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0 no matter the mean parameter.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * 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</code> and
+ * {@link #isSupportUpperBoundInclusive()} returns <code>true</code>.
+ *
+ * @return upper bound of the support (always <code>Integer.MAX_VALUE</code> for positive infinity)
+ * @since 2.2
+ */
+ public int getSupportUpperBound() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Returns the variance of the distribution.
+ *
+ * For mean parameter <code>p</code>, the variance is <code>p</code>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ return getMean();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/SaddlePointExpansion.java b/src/main/java/org/apache/commons/math/distribution/SaddlePointExpansion.java
new file mode 100644
index 0000000..a936123
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/SaddlePointExpansion.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.math.distribution;
+
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * <p>
+ * 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>
+ * <p>
+ * This class is not intended to be called directly.
+ * </p>
+ * <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></li>
+ * </ol>
+ * </p>
+ *
+ * @since 2.1
+ * @version $Revision$ $Date$
+ */
+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></li>
+ * </ol>
+ * </p>
+ *
+ * @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></li>
+ * </ol>
+ * </p>
+ *
+ * @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 * 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 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/math/distribution/TDistribution.java b/src/main/java/org/apache/commons/math/distribution/TDistribution.java
new file mode 100644
index 0000000..034c0d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/TDistribution.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.math.distribution;
+
+/**
+ * Student's t-Distribution.
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/Studentst-Distribution.html">
+ * Student's t-Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface TDistribution extends ContinuousDistribution {
+ /**
+ * Modify the degrees of freedom.
+ * @param degreesOfFreedom the new degrees of freedom.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setDegreesOfFreedom(double degreesOfFreedom);
+
+ /**
+ * Access the degrees of freedom.
+ * @return the degrees of freedom.
+ */
+ double getDegreesOfFreedom();
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/TDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/TDistributionImpl.java
new file mode 100644
index 0000000..35b72cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/TDistributionImpl.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Beta;
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Default implementation of
+ * {@link org.apache.commons.math.distribution.TDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class TDistributionImpl
+ extends AbstractContinuousDistribution
+ implements TDistribution, Serializable {
+
+ /**
+ * 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 double degreesOfFreedom;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a t distribution using the given degrees of freedom and the
+ * specified inverse cumulative probability absolute accuracy.
+ *
+ * @param degreesOfFreedom the 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 TDistributionImpl(double degreesOfFreedom, double inverseCumAccuracy) {
+ super();
+ setDegreesOfFreedomInternal(degreesOfFreedom);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Create a t distribution using the given degrees of freedom.
+ * @param degreesOfFreedom the degrees of freedom.
+ */
+ public TDistributionImpl(double degreesOfFreedom) {
+ this(degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Modify the degrees of freedom.
+ * @param degreesOfFreedom the new degrees of freedom.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setDegreesOfFreedom(double degreesOfFreedom) {
+ setDegreesOfFreedomInternal(degreesOfFreedom);
+ }
+
+ /**
+ * Modify the degrees of freedom.
+ * @param newDegreesOfFreedom the new degrees of freedom.
+ */
+ private void setDegreesOfFreedomInternal(double newDegreesOfFreedom) {
+ if (newDegreesOfFreedom <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_DEGREES_OF_FREEDOM,
+ newDegreesOfFreedom);
+ }
+ this.degreesOfFreedom = newDegreesOfFreedom;
+ }
+
+ /**
+ * Access the degrees of freedom.
+ * @return the degrees of freedom.
+ */
+ public double getDegreesOfFreedom() {
+ return degreesOfFreedom;
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ public double density(double x) {
+ final double n = degreesOfFreedom;
+ final double nPlus1Over2 = (n + 1) / 2;
+ return FastMath.exp(Gamma.logGamma(nPlus1Over2) - 0.5 * (FastMath.log(FastMath.PI) + FastMath.log(n)) -
+ Gamma.logGamma(n/2) - nPlus1Over2 * FastMath.log(1 + x * x /n));
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; <code>x</code>).
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF evaluated at <code>x</code>.
+ * @throws MathException if the cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ */
+ public double cumulativeProbability(double x) throws MathException{
+ double ret;
+ if (x == 0.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;
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and
+ * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws MathException if the inverse cumulative probability can not be
+ * computed due to convergence or other numerical errors.
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p)
+ throws MathException {
+ if (p == 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return super.inverseCumulativeProbability(p);
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return -Double.MAX_VALUE;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ return Double.MAX_VALUE;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ return 0.0;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always negative infinity
+ * no matter the parameters.
+ *
+ * @return lower bound of the support (always Double.NEGATIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity
+ * no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For degrees of freedom parameter df, the mean is
+ * <ul>
+ * <li>if <code>df &gt; 1</code> then <code>0</code></li>
+ * <li>else <code>undefined</code></li>
+ * </ul>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ final double df = getDegreesOfFreedom();
+
+ if (df > 1) {
+ return 0;
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For degrees of freedom parameter df, the variance is
+ * <ul>
+ * <li>if <code>df &gt; 2</code> then <code>df / (df - 2)</code> </li>
+ * <li>if <code>1 &lt; df &lt;= 2</code> then <code>positive infinity</code></li>
+ * <li>else <code>undefined</code></li>
+ * </ul>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ 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;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/WeibullDistribution.java b/src/main/java/org/apache/commons/math/distribution/WeibullDistribution.java
new file mode 100644
index 0000000..bd1df5e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/WeibullDistribution.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.math.distribution;
+
+/**
+ * Weibull Distribution. This interface defines the two parameter form of the
+ * distribution as defined by
+ * <a href="http://mathworld.wolfram.com/WeibullDistribution.html">
+ * Weibull Distribution</a>, equations (1) and (2).
+ *
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/WeibullDistribution.html">
+ * Weibull Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @since 1.1
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface WeibullDistribution extends ContinuousDistribution {
+
+ /**
+ * Access the shape parameter.
+ * @return the shape parameter.
+ */
+ double getShape();
+
+ /**
+ * Access the scale parameter.
+ * @return the scale parameter.
+ */
+ double getScale();
+
+ /**
+ * Modify the shape parameter.
+ * @param alpha The new shape parameter value.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setShape(double alpha);
+
+ /**
+ * Modify the scale parameter.
+ * @param beta The new scale parameter value.
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setScale(double beta);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/WeibullDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/WeibullDistributionImpl.java
new file mode 100644
index 0000000..c52caac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/WeibullDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Default implementation of
+ * {@link org.apache.commons.math.distribution.WeibullDistribution}.
+ *
+ * @since 1.1
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class WeibullDistributionImpl extends AbstractContinuousDistribution
+ implements WeibullDistribution, Serializable {
+
+ /**
+ * 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 double shape;
+
+ /** The scale parameter. */
+ private 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;
+
+ /**
+ * Creates weibull distribution with the given shape and scale and a
+ * location equal to zero.
+ * @param alpha the shape parameter.
+ * @param beta the scale parameter.
+ */
+ public WeibullDistributionImpl(double alpha, double beta){
+ this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates weibull distribution with the given shape, scale and inverse
+ * cumulative probability accuracy and a location equal to zero.
+ * @param alpha the shape parameter.
+ * @param beta the scale parameter.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
+ * @since 2.1
+ */
+ public WeibullDistributionImpl(double alpha, double beta, double inverseCumAccuracy){
+ super();
+ setShapeInternal(alpha);
+ setScaleInternal(beta);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X &lt; <code>x</code>).
+ * @param x the value at which the CDF is evaluated.
+ * @return CDF evaluated at <code>x</code>.
+ */
+ 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;
+ }
+
+ /**
+ * Access the shape parameter.
+ * @return the shape parameter.
+ */
+ public double getShape() {
+ return shape;
+ }
+
+ /**
+ * Access the scale parameter.
+ * @return the scale parameter.
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /**
+ * Returns the probability density for a particular point.
+ *
+ * @param x The point at which the density should be computed.
+ * @return The pdf at point x.
+ * @since 2.1
+ */
+ @Override
+ 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);
+ }
+
+ /**
+ * For this distribution, X, this method returns the critical point x, such
+ * that P(X &lt; x) = <code>p</code>.
+ * <p>
+ * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and
+ * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
+ *
+ * @param p the desired probability
+ * @return x, such that P(X &lt; x) = <code>p</code>
+ * @throws IllegalArgumentException if <code>p</code> is not a valid
+ * probability.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) {
+ double ret;
+ if (p < 0.0 || p > 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, 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.log(1.0 - p), 1.0 / shape);
+ }
+ return ret;
+ }
+
+ /**
+ * Modify the shape parameter.
+ * @param alpha the new shape parameter value.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setShape(double alpha) {
+ setShapeInternal(alpha);
+ invalidateParameterDependentMoments();
+ }
+ /**
+ * Modify the shape parameter.
+ * @param alpha the new shape parameter value.
+ */
+ private void setShapeInternal(double alpha) {
+ if (alpha <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_SHAPE,
+ alpha);
+ }
+ this.shape = alpha;
+ }
+
+ /**
+ * Modify the scale parameter.
+ * @param beta the new scale parameter value.
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setScale(double beta) {
+ setScaleInternal(beta);
+ invalidateParameterDependentMoments();
+ }
+ /**
+ * Modify the scale parameter.
+ * @param beta the new scale parameter value.
+ */
+ private void setScaleInternal(double beta) {
+ if (beta <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_SCALE,
+ beta);
+ }
+ this.scale = beta;
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected double getDomainLowerBound(double p) {
+ return 0.0;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ return Double.MAX_VALUE;
+ }
+
+ /**
+ * Access the initial domain value, based on <code>p</code>, used to
+ * bracket a CDF root. This method is used by
+ * {@link #inverseCumulativeProbability(double)} to find critical values.
+ *
+ * @param p the desired probability for the critical value
+ * @return initial domain value
+ */
+ @Override
+ protected double getInitialDomain(double p) {
+ // use median
+ return FastMath.pow(scale * FastMath.log(2.0), 1.0 / shape);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ * @since 2.2
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is always positive infinity
+ * no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ * @since 2.2
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /**
+ * Calculates the mean.
+ *
+ * The mean is <code>scale * Gamma(1 + (1 / shape))</code>
+ * where <code>Gamma(...)</code> is the Gamma-function
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ protected double calculateNumericalMean() {
+ final double sh = getShape();
+ final double sc = getScale();
+
+ return sc * FastMath.exp(Gamma.logGamma(1 + (1 / sh)));
+ }
+
+ /**
+ * Calculates the variance.
+ *
+ * The variance is
+ * <code>scale^2 * Gamma(1 + (2 / shape)) - mean^2</code>
+ * where <code>Gamma(...)</code> is the Gamma-function
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ private 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);
+ }
+
+ /**
+ * Returns the mean of the distribution.
+ *
+ * @return the mean or Double.NaN if it's not defined
+ * @since 2.2
+ */
+ public double getNumericalMean() {
+ if (!numericalMeanIsCalculated) {
+ numericalMean = calculateNumericalMean();
+ numericalMeanIsCalculated = true;
+ }
+
+ return numericalMean;
+ }
+
+ /**
+ * Returns the variance of the distribution.
+ *
+ * @return the variance (possibly Double.POSITIVE_INFINITY as
+ * for certain cases in {@link TDistributionImpl}) or
+ * Double.NaN if it's not defined
+ * @since 2.2
+ */
+ public double getNumericalVariance() {
+ if (!numericalVarianceIsCalculated) {
+ numericalVariance = calculateNumericalVariance();
+ numericalVarianceIsCalculated = true;
+ }
+
+ return numericalVariance;
+ }
+
+ /**
+ * Invalidates the cached mean and variance.
+ */
+ private void invalidateParameterDependentMoments() {
+ numericalMeanIsCalculated = false;
+ numericalVarianceIsCalculated = false;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ZipfDistribution.java b/src/main/java/org/apache/commons/math/distribution/ZipfDistribution.java
new file mode 100644
index 0000000..885adac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ZipfDistribution.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.math.distribution;
+
+/**
+ * The Zipf (or zeta) Distribution.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ZipfDistribution.html">Zipf
+ * Distribution</a></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 920852 $ $Date: 2010-03-09 13:53:44 +0100 (mar. 09 mars 2010) $
+ */
+public interface ZipfDistribution extends IntegerDistribution {
+
+ /**
+ * Get the number of elements (e.g. corpus size) for the distribution.
+ *
+ * @return the number of elements
+ */
+ int getNumberOfElements();
+
+ /**
+ * Set the number of elements (e.g. corpus size) for the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param n the number of elements
+ * @throws IllegalArgumentException if n &le; 0
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setNumberOfElements(int n);
+
+ /**
+ * Get the exponent characterising the distribution.
+ *
+ * @return the exponent
+ */
+ double getExponent();
+
+ /**
+ * Set the exponent characterising the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param s the exponent
+ * @throws IllegalArgumentException if s &le; 0.0
+ * @deprecated as of v2.1
+ */
+ @Deprecated
+ void setExponent(double s);
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/ZipfDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/ZipfDistributionImpl.java
new file mode 100644
index 0000000..9f19528
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/ZipfDistributionImpl.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.math.distribution;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implementation for the {@link ZipfDistribution}.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class ZipfDistributionImpl extends AbstractIntegerDistribution
+ implements ZipfDistribution, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -140627372283420404L;
+
+ /** Number of elements. */
+ private int numberOfElements;
+
+ /** Exponent parameter of the distribution. */
+ private double exponent;
+
+ /**
+ * Create a new Zipf distribution with the given number of elements and
+ * exponent. Both values must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param numberOfElements the number of elements
+ * @param exponent the exponent
+ * @exception IllegalArgumentException if n &le; 0 or s &le; 0.0
+ */
+ public ZipfDistributionImpl(final int numberOfElements, final double exponent)
+ throws IllegalArgumentException {
+ setNumberOfElementsInternal(numberOfElements);
+ setExponentInternal(exponent);
+ }
+
+ /**
+ * Get the number of elements (e.g. corpus size) for the distribution.
+ *
+ * @return the number of elements
+ */
+ public int getNumberOfElements() {
+ return numberOfElements;
+ }
+
+ /**
+ * Set the number of elements (e.g. corpus size) for the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param n the number of elements
+ * @exception IllegalArgumentException if n &le; 0
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setNumberOfElements(final int n) {
+ setNumberOfElementsInternal(n);
+ }
+ /**
+ * Set the number of elements (e.g. corpus size) for the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param n the number of elements
+ * @exception IllegalArgumentException if n &le; 0
+ */
+ private void setNumberOfElementsInternal(final int n)
+ throws IllegalArgumentException {
+ if (n <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, n, 0);
+ }
+ this.numberOfElements = n;
+ }
+
+ /**
+ * Get the exponent characterising the distribution.
+ *
+ * @return the exponent
+ */
+ public double getExponent() {
+ return exponent;
+ }
+
+ /**
+ * Set the exponent characterising the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param s the exponent
+ * @exception IllegalArgumentException if s &le; 0.0
+ * @deprecated as of 2.1 (class will become immutable in 3.0)
+ */
+ @Deprecated
+ public void setExponent(final double s) {
+ setExponentInternal(s);
+ }
+
+ /**
+ * Set the exponent characterising the distribution.
+ * The parameter value must be positive; otherwise an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param s the exponent
+ * @exception IllegalArgumentException if s &le; 0.0
+ */
+ private void setExponentInternal(final double s)
+ throws IllegalArgumentException {
+ if (s <= 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_EXPONENT,
+ s);
+ }
+ this.exponent = s;
+ }
+
+ /**
+ * The probability mass function P(X = x) for a Zipf distribution.
+ *
+ * @param x the value at which the probability density function is evaluated.
+ * @return the value of the probability mass function at x
+ */
+ public double probability(final int x) {
+ if (x <= 0 || x > numberOfElements) {
+ return 0.0;
+ }
+
+ return (1.0 / FastMath.pow(x, exponent)) / generalizedHarmonic(numberOfElements, exponent);
+
+ }
+
+ /**
+ * The probability distribution function P(X <= x) for a Zipf distribution.
+ *
+ * @param x the value at which the PDF is evaluated.
+ * @return Zipf distribution function evaluated at x
+ */
+ @Override
+ 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);
+
+ }
+
+ /**
+ * Access the domain value lower bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value lower bound, i.e.
+ * P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
+ */
+ @Override
+ protected int getDomainLowerBound(final double p) {
+ return 0;
+ }
+
+ /**
+ * Access the domain value upper bound, based on <code>p</code>, used to
+ * bracket a PDF root.
+ *
+ * @param p the desired probability for the critical value
+ * @return domain value upper bound, i.e.
+ * P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
+ */
+ @Override
+ protected int getDomainUpperBound(final double p) {
+ return numberOfElements;
+ }
+
+
+ /**
+ * Calculates the Nth generalized harmonic number. See
+ * <a href="http://mathworld.wolfram.com/HarmonicSeries.html">Harmonic
+ * Series</a>.
+ *
+ * @param n the term in the series to calculate (must be &ge; 1)
+ * @param m the exponent; special case m == 1.0 is the harmonic series
+ * @return the nth 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;
+ }
+
+ /**
+ * Returns the lower bound of the support for the distribution.
+ *
+ * The lower bound of the support is always 1 no matter the parameters.
+ *
+ * @return lower bound of the support (always 1)
+ * @since 2.2
+ */
+ public int getSupportLowerBound() {
+ return 1;
+ }
+
+ /**
+ * Returns the upper bound of the support for the distribution.
+ *
+ * The upper bound of the support is the number of elements
+ *
+ * @return upper bound of the support
+ * @since 2.2
+ */
+ public int getSupportUpperBound() {
+ return getNumberOfElements();
+ }
+
+ /**
+ * Returns the mean.
+ *
+ * For number of elements N and exponent s, the mean is
+ * <code>Hs1 / Hs</code> where
+ * <ul>
+ * <li><code>Hs1 = generalizedHarmonic(N, s - 1)</code></li>
+ * <li><code>Hs = generalizedHarmonic(N, s)</code></li>
+ * </ul>
+ *
+ * @return the mean
+ * @since 2.2
+ */
+ protected double getNumericalMean() {
+ final int N = getNumberOfElements();
+ final double s = getExponent();
+
+ final double Hs1 = generalizedHarmonic(N, s - 1);
+ final double Hs = generalizedHarmonic(N, s);
+
+ return Hs1 / Hs;
+ }
+
+ /**
+ * Returns the variance.
+ *
+ * For number of elements N and exponent s, the mean is
+ * <code>(Hs2 / Hs) - (Hs1^2 / Hs^2)</code> where
+ * <ul>
+ * <li><code>Hs2 = generalizedHarmonic(N, s - 2)</code></li>
+ * <li><code>Hs1 = generalizedHarmonic(N, s - 1)</code></li>
+ * <li><code>Hs = generalizedHarmonic(N, s)</code></li>
+ * </ul>
+ *
+ * @return the variance
+ * @since 2.2
+ */
+ protected double getNumericalVariance() {
+ 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));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/distribution/package.html b/src/main/java/org/apache/commons/math/distribution/package.html
new file mode 100644
index 0000000..c89e8d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/distribution/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Implementations of common discrete and continuous distributions.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/estimation/AbstractEstimator.java b/src/main/java/org/apache/commons/math/estimation/AbstractEstimator.java
new file mode 100644
index 0000000..7a52b55
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/AbstractEstimator.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.math.estimation;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.InvalidMatrixException;
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Base class for implementing estimators.
+ * <p>This base class handles the boilerplates methods associated to thresholds
+ * settings, jacobian and error estimation.</p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public abstract class AbstractEstimator implements Estimator {
+
+ /** Default maximal number of cost evaluations allowed. */
+ public static final int DEFAULT_MAX_COST_EVALUATIONS = 100;
+
+ /** Array of measurements. */
+ protected WeightedMeasurement[] measurements;
+
+ /** Array of parameters. */
+ protected EstimatedParameter[] parameters;
+
+ /**
+ * Jacobian matrix.
+ * <p>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 LevenbergMarquardtEstimator
+ * Levenberg-Marquardt estimator} does this).</p>
+ */
+ protected double[] jacobian;
+
+ /** Number of columns of the jacobian matrix. */
+ protected int cols;
+
+ /** Number of rows of the jacobian matrix. */
+ protected int rows;
+
+ /** Residuals array.
+ * <p>This array is in canonical form just after the calls to
+ * {@link #updateJacobian()}, but may be modified by the solver
+ * in the derived class (the {@link LevenbergMarquardtEstimator
+ * Levenberg-Marquardt estimator} does this).</p>
+ */
+ protected double[] residuals;
+
+ /** Cost value (square root of the sum of the residuals). */
+ protected double cost;
+
+ /** Maximal allowed number of cost evaluations. */
+ private int maxCostEval;
+
+ /** Number of cost evaluations. */
+ private int costEvaluations;
+
+ /** Number of jacobian evaluations. */
+ private int jacobianEvaluations;
+
+ /**
+ * Build an abstract estimator for least squares problems.
+ * <p>The maximal number of cost evaluations allowed is set
+ * to its default value {@link #DEFAULT_MAX_COST_EVALUATIONS}.</p>
+ */
+ protected AbstractEstimator() {
+ setMaxCostEval(DEFAULT_MAX_COST_EVALUATIONS);
+ }
+
+ /**
+ * Set the maximal number of cost evaluations allowed.
+ *
+ * @param maxCostEval maximal number of cost evaluations allowed
+ * @see #estimate
+ */
+ public final void setMaxCostEval(int maxCostEval) {
+ this.maxCostEval = maxCostEval;
+ }
+
+ /**
+ * Get the number of cost evaluations.
+ *
+ * @return number of cost evaluations
+ * */
+ public final int getCostEvaluations() {
+ return costEvaluations;
+ }
+
+ /**
+ * Get the number of jacobian evaluations.
+ *
+ * @return number of jacobian evaluations
+ * */
+ public final int getJacobianEvaluations() {
+ return jacobianEvaluations;
+ }
+
+ /**
+ * Update the jacobian matrix.
+ */
+ protected void updateJacobian() {
+ incrementJacobianEvaluationsCounter();
+ Arrays.fill(jacobian, 0);
+ int index = 0;
+ for (int i = 0; i < rows; i++) {
+ WeightedMeasurement wm = measurements[i];
+ double factor = -FastMath.sqrt(wm.getWeight());
+ for (int j = 0; j < cols; ++j) {
+ jacobian[index++] = factor * wm.getPartial(parameters[j]);
+ }
+ }
+ }
+
+ /**
+ * Increment the jacobian evaluations counter.
+ */
+ protected final void incrementJacobianEvaluationsCounter() {
+ ++jacobianEvaluations;
+ }
+
+ /**
+ * Update the residuals array and cost function value.
+ * @exception EstimationException if the number of cost evaluations
+ * exceeds the maximum allowed
+ */
+ protected void updateResidualsAndCost()
+ throws EstimationException {
+
+ if (++costEvaluations > maxCostEval) {
+ throw new EstimationException(LocalizedFormats.MAX_EVALUATIONS_EXCEEDED,
+ maxCostEval);
+ }
+
+ cost = 0;
+ int index = 0;
+ for (int i = 0; i < rows; i++, index += cols) {
+ WeightedMeasurement wm = measurements[i];
+ double residual = wm.getResidual();
+ residuals[i] = FastMath.sqrt(wm.getWeight()) * residual;
+ cost += wm.getWeight() * residual * residual;
+ }
+ cost = FastMath.sqrt(cost);
+
+ }
+
+ /**
+ * 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 estimator 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>.
+ *
+ * @param problem estimation problem
+ * @return RMS value
+ */
+ public double getRMS(EstimationProblem problem) {
+ WeightedMeasurement[] wm = problem.getMeasurements();
+ double criterion = 0;
+ for (int i = 0; i < wm.length; ++i) {
+ double residual = wm[i].getResidual();
+ criterion += wm[i].getWeight() * residual * residual;
+ }
+ return FastMath.sqrt(criterion / wm.length);
+ }
+
+ /**
+ * Get the Chi-Square value.
+ * @param problem estimation problem
+ * @return chi-square value
+ */
+ public double getChiSquare(EstimationProblem problem) {
+ WeightedMeasurement[] wm = problem.getMeasurements();
+ double chiSquare = 0;
+ for (int i = 0; i < wm.length; ++i) {
+ double residual = wm[i].getResidual();
+ chiSquare += residual * residual / wm[i].getWeight();
+ }
+ return chiSquare;
+ }
+
+ /**
+ * Get the covariance matrix of unbound estimated parameters.
+ * @param problem estimation problem
+ * @return covariance matrix
+ * @exception EstimationException if the covariance matrix
+ * cannot be computed (singular problem)
+ */
+ public double[][] getCovariances(EstimationProblem problem)
+ throws EstimationException {
+
+ // set up the jacobian
+ updateJacobian();
+
+ // compute transpose(J).J, avoiding building big intermediate matrices
+ final int n = problem.getMeasurements().length;
+ final int m = problem.getUnboundParameters().length;
+ final int max = m * n;
+ double[][] jTj = new double[m][m];
+ for (int i = 0; i < m; ++i) {
+ for (int j = i; j < m; ++j) {
+ double sum = 0;
+ for (int k = 0; k < max; k += m) {
+ sum += jacobian[k + i] * jacobian[k + j];
+ }
+ jTj[i][j] = sum;
+ jTj[j][i] = sum;
+ }
+ }
+
+ try {
+ // compute the covariances matrix
+ RealMatrix inverse =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(jTj)).getSolver().getInverse();
+ return inverse.getData();
+ } catch (InvalidMatrixException ime) {
+ throw new EstimationException(LocalizedFormats.UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM);
+ }
+
+ }
+
+ /**
+ * Guess the errors in unbound estimated parameters.
+ * <p>Guessing is covariance-based, it only gives rough order of magnitude.</p>
+ * @param problem estimation problem
+ * @return errors in estimated parameters
+ * @exception EstimationException if the covariances matrix cannot be computed
+ * or the number of degrees of freedom is not positive (number of measurements
+ * lesser or equal to number of parameters)
+ */
+ public double[] guessParametersErrors(EstimationProblem problem)
+ throws EstimationException {
+ int m = problem.getMeasurements().length;
+ int p = problem.getUnboundParameters().length;
+ if (m <= p) {
+ throw new EstimationException(
+ LocalizedFormats.NO_DEGREES_OF_FREEDOM,
+ m, p);
+ }
+ double[] errors = new double[problem.getUnboundParameters().length];
+ final double c = FastMath.sqrt(getChiSquare(problem) / (m - p));
+ double[][] covar = getCovariances(problem);
+ for (int i = 0; i < errors.length; ++i) {
+ errors[i] = FastMath.sqrt(covar[i][i]) * c;
+ }
+ return errors;
+ }
+
+ /**
+ * Initialization of the common parts of the estimation.
+ * <p>This method <em>must</em> be called at the start
+ * of the {@link #estimate(EstimationProblem) estimate}
+ * method.</p>
+ * @param problem estimation problem to solve
+ */
+ protected void initializeEstimate(EstimationProblem problem) {
+
+ // reset counters
+ costEvaluations = 0;
+ jacobianEvaluations = 0;
+
+ // retrieve the equations and the parameters
+ measurements = problem.getMeasurements();
+ parameters = problem.getUnboundParameters();
+
+ // arrays shared with the other private methods
+ rows = measurements.length;
+ cols = parameters.length;
+ jacobian = new double[rows * cols];
+ residuals = new double[rows];
+
+ cost = Double.POSITIVE_INFINITY;
+
+ }
+
+ /**
+ * Solve an estimation problem.
+ *
+ * <p>The method should set the parameters of the problem to several
+ * trial values until it reaches convergence. If this method returns
+ * normally (i.e. without throwing an exception), then the best
+ * estimate of the parameters can be retrieved from the problem
+ * itself, through the {@link EstimationProblem#getAllParameters
+ * EstimationProblem.getAllParameters} method.</p>
+ *
+ * @param problem estimation problem to solve
+ * @exception EstimationException if the problem cannot be solved
+ *
+ */
+ public abstract void estimate(EstimationProblem problem)
+ throws EstimationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/EstimatedParameter.java b/src/main/java/org/apache/commons/math/estimation/EstimatedParameter.java
new file mode 100644
index 0000000..e0047bb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/EstimatedParameter.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.estimation;
+
+import java.io.Serializable;
+
+/** This class represents the estimated parameters of an estimation problem.
+ *
+ * <p>The parameters of an estimation problem have a name, a value and
+ * a bound flag. The value of bound parameters is considered trusted
+ * and the solvers should not adjust them. On the other hand, the
+ * solvers should adjust the value of unbounds parameters until they
+ * satisfy convergence criterions specific to each solver.</p>
+ *
+ * @version $Revision: 922710 $ $Date: 2010-03-14 02:20:56 +0100 (dim. 14 mars 2010) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public class EstimatedParameter
+ implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -555440800213416949L;
+
+ /** Current value of the parameter */
+ protected double estimate;
+
+ /** Name of the parameter */
+ private final String name;
+
+ /** Indicator for bound parameters
+ * (ie parameters that should not be estimated)
+ */
+ private boolean bound;
+
+ /** Simple constructor.
+ * Build an instance from a first estimate of the parameter,
+ * initially considered unbound.
+ * @param name name of the parameter
+ * @param firstEstimate first estimate of the parameter
+ */
+ public EstimatedParameter(String name, double firstEstimate) {
+ this.name = name;
+ estimate = firstEstimate;
+ bound = false;
+ }
+
+ /** Simple constructor.
+ * Build an instance from a first estimate of the parameter and a
+ * bound flag
+ * @param name name of the parameter
+ * @param firstEstimate first estimate of the parameter
+ * @param bound flag, should be true if the parameter is bound
+ */
+ public EstimatedParameter(String name,
+ double firstEstimate,
+ boolean bound) {
+ this.name = name;
+ estimate = firstEstimate;
+ this.bound = bound;
+ }
+
+ /** Copy constructor.
+ * Build a copy of a parameter
+ * @param parameter instance to copy
+ */
+ public EstimatedParameter(EstimatedParameter parameter) {
+ name = parameter.name;
+ estimate = parameter.estimate;
+ bound = parameter.bound;
+ }
+
+ /** Set a new estimated value for the parameter.
+ * @param estimate new estimate for the parameter
+ */
+ public void setEstimate(double estimate) {
+ this.estimate = estimate;
+ }
+
+ /** Get the current estimate of the parameter
+ * @return current estimate
+ */
+ public double getEstimate() {
+ return estimate;
+ }
+
+ /** get the name of the parameter
+ * @return parameter name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /** Set the bound flag of the parameter
+ * @param bound this flag should be set to true if the parameter is
+ * bound (i.e. if it should not be adjusted by the solver).
+ */
+ public void setBound(boolean bound) {
+ this.bound = bound;
+ }
+
+ /** Check if the parameter is bound
+ * @return true if the parameter is bound */
+ public boolean isBound() {
+ return bound;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/EstimationException.java b/src/main/java/org/apache/commons/math/estimation/EstimationException.java
new file mode 100644
index 0000000..463ea49
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/EstimationException.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.math.estimation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This class represents exceptions thrown by the estimation solvers.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public class EstimationException
+extends MathException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -573038581493881337L;
+
+ /**
+ * 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)
+ */
+ public EstimationException(String specifier, Object ... parts) {
+ this(new DummyLocalizable(specifier), parts);
+ }
+
+ /**
+ * 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 EstimationException(Localizable specifier, Object ... parts) {
+ super(specifier, parts);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/EstimationProblem.java b/src/main/java/org/apache/commons/math/estimation/EstimationProblem.java
new file mode 100644
index 0000000..a0d660b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/EstimationProblem.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.math.estimation;
+
+/**
+ * This interface represents an estimation problem.
+ *
+ * <p>This interface should be implemented by all real estimation
+ * problems before they can be handled by the estimators through the
+ * {@link Estimator#estimate Estimator.estimate} method.</p>
+ *
+ * <p>An estimation problem, as seen by a solver is a set of
+ * parameters and a set of measurements. The parameters are adjusted
+ * during the estimation through the {@link #getUnboundParameters
+ * getUnboundParameters} and {@link EstimatedParameter#setEstimate
+ * EstimatedParameter.setEstimate} methods. The measurements both have
+ * a measured value which is generally fixed at construction and a
+ * theoretical value which depends on the model and hence varies as
+ * the parameters are adjusted. The purpose of the solver is to reduce
+ * the residual between these values, it can retrieve the measurements
+ * through the {@link #getMeasurements getMeasurements} method.</p>
+ *
+ * @see Estimator
+ * @see WeightedMeasurement
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public interface EstimationProblem {
+
+ /**
+ * Get the measurements of an estimation problem.
+ * @return measurements
+ */
+ WeightedMeasurement[] getMeasurements();
+
+ /**
+ * Get the unbound parameters of the problem.
+ * @return unbound parameters
+ */
+ EstimatedParameter[] getUnboundParameters();
+
+ /**
+ * Get all the parameters of the problem.
+ * @return parameters
+ */
+ EstimatedParameter[] getAllParameters();
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/Estimator.java b/src/main/java/org/apache/commons/math/estimation/Estimator.java
new file mode 100644
index 0000000..5d6d85a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/Estimator.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.math.estimation;
+
+/**
+ * This interface represents solvers for estimation problems.
+ *
+ * <p>The classes which are devoted to solve estimation problems
+ * should implement this interface. The problems which can be handled
+ * should implement the {@link EstimationProblem} interface which
+ * gather all the information needed by the solver.</p>
+ *
+ * <p>The interface is composed only of the {@link #estimate estimate}
+ * method.</p>
+ *
+ * @see EstimationProblem
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public interface Estimator {
+
+ /**
+ * Solve an estimation problem.
+ *
+ * <p>The method should set the parameters of the problem to several
+ * trial values until it reaches convergence. If this method returns
+ * normally (i.e. without throwing an exception), then the best
+ * estimate of the parameters can be retrieved from the problem
+ * itself, through the {@link EstimationProblem#getAllParameters
+ * EstimationProblem.getAllParameters} method.</p>
+ *
+ * @param problem estimation problem to solve
+ * @exception EstimationException if the problem cannot be solved
+ *
+ */
+ void estimate(EstimationProblem problem) throws EstimationException;
+
+ /**
+ * 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 estimator as follows: if
+ * <em>c</em> is the criterion, and <em>n</em> is the number of
+ * measurements, then the RMS is <em>sqrt (c/n)</em>.
+ * @see #guessParametersErrors(EstimationProblem)
+ *
+ * @param problem estimation problem
+ * @return RMS value
+ */
+ double getRMS(EstimationProblem problem);
+
+ /**
+ * Get the covariance matrix of estimated parameters.
+ * @param problem estimation problem
+ * @return covariance matrix
+ * @exception EstimationException if the covariance matrix
+ * cannot be computed (singular problem)
+ */
+ double[][] getCovariances(EstimationProblem problem) throws EstimationException;
+
+ /**
+ * Guess the errors in estimated parameters.
+ * @see #getRMS(EstimationProblem)
+ * @param problem estimation problem
+ * @return errors in estimated parameters
+ * @exception EstimationException if the error cannot be guessed
+ */
+ double[] guessParametersErrors(EstimationProblem problem) throws EstimationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java b/src/main/java/org/apache/commons/math/estimation/GaussNewtonEstimator.java
new file mode 100644
index 0000000..c5d0dd3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/GaussNewtonEstimator.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.math.estimation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.InvalidMatrixException;
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.linear.ArrayRealVector;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class implements a solver for estimation problems.
+ *
+ * <p>This class solves estimation problems using a weighted least
+ * squares criterion on the measurement residuals. It uses a
+ * Gauss-Newton algorithm.</p>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public class GaussNewtonEstimator extends AbstractEstimator implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5485001826076289109L;
+
+ /** Default threshold for cost steady state detection. */
+ private static final double DEFAULT_STEADY_STATE_THRESHOLD = 1.0e-6;
+
+ /** Default threshold for cost convergence. */
+ private static final double DEFAULT_CONVERGENCE = 1.0e-6;
+
+ /** Threshold for cost steady state detection. */
+ private double steadyStateThreshold;
+
+ /** Threshold for cost convergence. */
+ private double convergence;
+
+ /** Simple constructor with default settings.
+ * <p>
+ * The estimator is built with default values for all settings.
+ * </p>
+ * @see #DEFAULT_STEADY_STATE_THRESHOLD
+ * @see #DEFAULT_CONVERGENCE
+ * @see AbstractEstimator#DEFAULT_MAX_COST_EVALUATIONS
+ */
+ public GaussNewtonEstimator() {
+ this.steadyStateThreshold = DEFAULT_STEADY_STATE_THRESHOLD;
+ this.convergence = DEFAULT_CONVERGENCE;
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * <p>This constructor builds an estimator and stores its convergence
+ * characteristics.</p>
+ *
+ * <p>An estimator is considered to have converged whenever either
+ * the criterion goes below a physical threshold under which
+ * improvements are considered useless or when the algorithm is
+ * unable to improve it (even if it is still high). The first
+ * condition that is met stops the iterations.</p>
+ *
+ * <p>The fact an estimator has converged does not mean that the
+ * model accurately fits the measurements. It only means no better
+ * solution can be found, it does not mean this one is good. Such an
+ * analysis is left to the caller.</p>
+ *
+ * <p>If neither conditions are fulfilled before a given number of
+ * iterations, the algorithm is considered to have failed and an
+ * {@link EstimationException} is thrown.</p>
+ *
+ * @param maxCostEval maximal number of cost evaluations allowed
+ * @param convergence criterion threshold below which we do not need
+ * to improve the criterion anymore
+ * @param steadyStateThreshold steady state detection threshold, the
+ * problem has converged has reached a steady state if
+ * <code>FastMath.abs(J<sub>n</sub> - J<sub>n-1</sub>) &lt;
+ * J<sub>n</sub> &times convergence</code>, where <code>J<sub>n</sub></code>
+ * and <code>J<sub>n-1</sub></code> are the current and preceding criterion
+ * values (square sum of the weighted residuals of considered measurements).
+ */
+ public GaussNewtonEstimator(final int maxCostEval, final double convergence,
+ final double steadyStateThreshold) {
+ setMaxCostEval(maxCostEval);
+ this.steadyStateThreshold = steadyStateThreshold;
+ this.convergence = convergence;
+ }
+
+ /**
+ * Set the convergence criterion threshold.
+ * @param convergence criterion threshold below which we do not need
+ * to improve the criterion anymore
+ */
+ public void setConvergence(final double convergence) {
+ this.convergence = convergence;
+ }
+
+ /**
+ * Set the steady state detection threshold.
+ * <p>
+ * The problem has converged has reached a steady state if
+ * <code>FastMath.abs(J<sub>n</sub> - J<sub>n-1</sub>) &lt;
+ * J<sub>n</sub> &times convergence</code>, where <code>J<sub>n</sub></code>
+ * and <code>J<sub>n-1</sub></code> are the current and preceding criterion
+ * values (square sum of the weighted residuals of considered measurements).
+ * </p>
+ * @param steadyStateThreshold steady state detection threshold
+ */
+ public void setSteadyStateThreshold(final double steadyStateThreshold) {
+ this.steadyStateThreshold = steadyStateThreshold;
+ }
+
+ /**
+ * Solve an estimation problem using a least squares criterion.
+ *
+ * <p>This method set the unbound parameters of the given problem
+ * starting from their current values through several iterations. At
+ * each step, the unbound parameters are changed in order to
+ * minimize a weighted least square criterion based on the
+ * measurements of the problem.</p>
+ *
+ * <p>The iterations are stopped either when the criterion goes
+ * below a physical threshold under which improvement are considered
+ * useless or when the algorithm is unable to improve it (even if it
+ * is still high). The first condition that is met stops the
+ * iterations. If the convergence it not reached before the maximum
+ * number of iterations, an {@link EstimationException} is
+ * thrown.</p>
+ *
+ * @param problem estimation problem to solve
+ * @exception EstimationException if the problem cannot be solved
+ *
+ * @see EstimationProblem
+ *
+ */
+ @Override
+ public void estimate(EstimationProblem problem)
+ throws EstimationException {
+
+ initializeEstimate(problem);
+
+ // work matrices
+ double[] grad = new double[parameters.length];
+ ArrayRealVector bDecrement = new ArrayRealVector(parameters.length);
+ double[] bDecrementData = bDecrement.getDataRef();
+ RealMatrix wGradGradT = MatrixUtils.createRealMatrix(parameters.length, parameters.length);
+
+ // iterate until convergence is reached
+ double previous = Double.POSITIVE_INFINITY;
+ do {
+
+ // build the linear problem
+ incrementJacobianEvaluationsCounter();
+ RealVector b = new ArrayRealVector(parameters.length);
+ RealMatrix a = MatrixUtils.createRealMatrix(parameters.length, parameters.length);
+ for (int i = 0; i < measurements.length; ++i) {
+ if (! measurements [i].isIgnored()) {
+
+ double weight = measurements[i].getWeight();
+ double residual = measurements[i].getResidual();
+
+ // compute the normal equation
+ for (int j = 0; j < parameters.length; ++j) {
+ grad[j] = measurements[i].getPartial(parameters[j]);
+ bDecrementData[j] = weight * residual * grad[j];
+ }
+
+ // build the contribution matrix for measurement i
+ for (int k = 0; k < parameters.length; ++k) {
+ double gk = grad[k];
+ for (int l = 0; l < parameters.length; ++l) {
+ wGradGradT.setEntry(k, l, weight * gk * grad[l]);
+ }
+ }
+
+ // update the matrices
+ a = a.add(wGradGradT);
+ b = b.add(bDecrement);
+
+ }
+ }
+
+ try {
+
+ // solve the linearized least squares problem
+ RealVector dX = new LUDecompositionImpl(a).getSolver().solve(b);
+
+ // update the estimated parameters
+ for (int i = 0; i < parameters.length; ++i) {
+ parameters[i].setEstimate(parameters[i].getEstimate() + dX.getEntry(i));
+ }
+
+ } catch(InvalidMatrixException e) {
+ throw new EstimationException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM);
+ }
+
+
+ previous = cost;
+ updateResidualsAndCost();
+
+ } while ((getCostEvaluations() < 2) ||
+ (FastMath.abs(previous - cost) > (cost * steadyStateThreshold) &&
+ (FastMath.abs(cost) > convergence)));
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java b/src/main/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java
new file mode 100644
index 0000000..78a4661
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimator.java
@@ -0,0 +1,897 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.estimation;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * This class solves a least squares problem.
+ *
+ * <p>This implementation <em>should</em> work even for over-determined systems
+ * (i.e. systems having more variables than equations). Over-determined systems
+ * are solved by ignoring the variables 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 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>
+
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ *
+ */
+@Deprecated
+public class LevenbergMarquardtEstimator extends AbstractEstimator implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -5705952631533171019L;
+
+ /** Number of solved variables. */
+ 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 double initialStepBoundFactor;
+
+ /** Desired relative error in the sum of squares. */
+ private double costRelativeTolerance;
+
+ /** Desired relative error in the approximate solution parameters. */
+ private double parRelativeTolerance;
+
+ /** Desired max cosine on the orthogonality between the function vector
+ * and the columns of the jacobian. */
+ private double orthoTolerance;
+
+ /**
+ * Build an estimator for least squares problems.
+ * <p>The default values for the algorithm settings are:
+ * <ul>
+ * <li>{@link #setInitialStepBoundFactor initial step bound factor}: 100.0</li>
+ * <li>{@link #setMaxCostEval maximal cost evaluations}: 1000</li>
+ * <li>{@link #setCostRelativeTolerance cost relative tolerance}: 1.0e-10</li>
+ * <li>{@link #setParRelativeTolerance parameters relative tolerance}: 1.0e-10</li>
+ * <li>{@link #setOrthoTolerance orthogonality tolerance}: 1.0e-10</li>
+ * </ul>
+ * </p>
+ */
+ public LevenbergMarquardtEstimator() {
+
+ // set up the superclass with a default max cost evaluations setting
+ setMaxCostEval(1000);
+
+ // default values for the tuning parameters
+ setInitialStepBoundFactor(100.0);
+ setCostRelativeTolerance(1.0e-10);
+ setParRelativeTolerance(1.0e-10);
+ setOrthoTolerance(1.0e-10);
+
+ }
+
+ /**
+ * Set the positive input variable used in determining the initial step bound.
+ * This bound is set to the product of initialStepBoundFactor and the euclidean norm of diag*x if nonzero,
+ * or else to initialStepBoundFactor itself. In most cases factor should lie
+ * in the interval (0.1, 100.0). 100.0 is a generally recommended value
+ *
+ * @param initialStepBoundFactor initial step bound factor
+ * @see #estimate
+ */
+ public void setInitialStepBoundFactor(double initialStepBoundFactor) {
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ }
+
+ /**
+ * Set the desired relative error in the sum of squares.
+ *
+ * @param costRelativeTolerance desired relative error in the sum of squares
+ * @see #estimate
+ */
+ public void setCostRelativeTolerance(double costRelativeTolerance) {
+ this.costRelativeTolerance = costRelativeTolerance;
+ }
+
+ /**
+ * Set the desired relative error in the approximate solution parameters.
+ *
+ * @param parRelativeTolerance desired relative error
+ * in the approximate solution parameters
+ * @see #estimate
+ */
+ public void setParRelativeTolerance(double parRelativeTolerance) {
+ this.parRelativeTolerance = parRelativeTolerance;
+ }
+
+ /**
+ * Set the desired max cosine on the orthogonality.
+ *
+ * @param orthoTolerance desired max cosine on the orthogonality
+ * between the function vector and the columns of the jacobian
+ * @see #estimate
+ */
+ public void setOrthoTolerance(double orthoTolerance) {
+ this.orthoTolerance = orthoTolerance;
+ }
+
+ /**
+ * Solve an estimation problem using the Levenberg-Marquardt algorithm.
+ * <p>The algorithm used is a modified Levenberg-Marquardt one, based
+ * on the MINPACK <a href="http://www.netlib.org/minpack/lmder.f">lmder</a>
+ * routine. The algorithm settings must have been set up before this method
+ * is called with the {@link #setInitialStepBoundFactor},
+ * {@link #setMaxCostEval}, {@link #setCostRelativeTolerance},
+ * {@link #setParRelativeTolerance} and {@link #setOrthoTolerance} methods.
+ * If these methods have not been called, the default values set up by the
+ * {@link #LevenbergMarquardtEstimator() constructor} will be used.</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 problem estimation problem to solve
+ * @exception EstimationException if convergence cannot be
+ * reached with the specified algorithm settings or if there are more variables
+ * than equations
+ * @see #setInitialStepBoundFactor
+ * @see #setCostRelativeTolerance
+ * @see #setParRelativeTolerance
+ * @see #setOrthoTolerance
+ */
+ @Override
+ public void estimate(EstimationProblem problem)
+ throws EstimationException {
+
+ initializeEstimate(problem);
+
+ // arrays shared with the other private methods
+ solvedCols = FastMath.min(rows, cols);
+ diagR = new double[cols];
+ jacNorm = new double[cols];
+ beta = new double[cols];
+ permutation = new int[cols];
+ lmDir = new double[cols];
+
+ // local variables
+ double delta = 0;
+ double xNorm = 0;
+ double[] diag = new double[cols];
+ double[] oldX = new double[cols];
+ double[] oldRes = new double[rows];
+ double[] work1 = new double[cols];
+ double[] work2 = new double[cols];
+ double[] work3 = new double[cols];
+
+ // evaluate the function at the starting point and calculate its norm
+ updateResidualsAndCost();
+
+ // outer loop
+ lmPar = 0;
+ boolean firstIteration = true;
+ while (true) {
+
+ // compute the Q.R. decomposition of the jacobian matrix
+ updateJacobian();
+ qrDecomposition();
+
+ // compute Qt.res
+ qTy(residuals);
+
+ // 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];
+ jacobian[k * cols + pk] = diagR[pk];
+ }
+
+ if (firstIteration) {
+
+ // scale the variables according to the norms of the columns
+ // of the initial jacobian
+ xNorm = 0;
+ for (int k = 0; k < cols; ++k) {
+ double dk = jacNorm[k];
+ if (dk == 0) {
+ dk = 1.0;
+ }
+ double xk = dk * parameters[k].getEstimate();
+ 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 (cost != 0) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = jacNorm[pj];
+ if (s != 0) {
+ double sum = 0;
+ int index = pj;
+ for (int i = 0; i <= j; ++i) {
+ sum += jacobian[index] * residuals[i];
+ index += cols;
+ }
+ maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * cost));
+ }
+ }
+ }
+ if (maxCosine <= orthoTolerance) {
+ return;
+ }
+
+ // rescale if necessary
+ for (int j = 0; j < cols; ++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] = parameters[pj].getEstimate();
+ }
+ double previousCost = cost;
+ double[] tmpVec = residuals;
+ residuals = oldRes;
+ oldRes = tmpVec;
+
+ // determine the Levenberg-Marquardt parameter
+ determineLMParameter(oldRes, 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];
+ parameters[pj].setEstimate(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
+ updateResidualsAndCost();
+
+ // compute the scaled actual reduction
+ double actRed = -1.0;
+ if (0.1 * cost < previousCost) {
+ double r = cost / 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;
+ int index = pj;
+ for (int i = 0; i <= j; ++i) {
+ work1[i] += jacobian[index] * dirJ;
+ index += cols;
+ }
+ }
+ double coeff1 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ coeff1 += work1[j] * work1[j];
+ }
+ double pc2 = previousCost * previousCost;
+ coeff1 = 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 * cost >= 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 < cols; ++k) {
+ double xK = diag[k] * parameters[k].getEstimate();
+ xNorm += xK * xK;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+ } else {
+ // failed iteration, reset the previous values
+ cost = previousCost;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ parameters[pj].setEstimate(oldX[pj]);
+ }
+ tmpVec = residuals;
+ residuals = oldRes;
+ oldRes = tmpVec;
+ }
+
+ // tests for convergence.
+ if (((FastMath.abs(actRed) <= costRelativeTolerance) &&
+ (preRed <= costRelativeTolerance) &&
+ (ratio <= 2.0)) ||
+ (delta <= parRelativeTolerance * xNorm)) {
+ return;
+ }
+
+ // 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 EstimationException("cost relative tolerance is too small ({0})," +
+ " no further reduction in the" +
+ " sum of squares is possible",
+ costRelativeTolerance);
+ } else if (delta <= 2.2204e-16 * xNorm) {
+ throw new EstimationException("parameters relative tolerance is too small" +
+ " ({0}), no further improvement in" +
+ " the approximate solution is possible",
+ parRelativeTolerance);
+ } else if (maxCosine <= 2.2204e-16) {
+ throw new EstimationException("orthogonality tolerance is too small ({0})," +
+ " solution is orthogonal to the jacobian",
+ 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) {
+
+ // 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 < cols; ++j) {
+ lmDir[permutation[j]] = 0;
+ }
+ for (int k = rank - 1; k >= 0; --k) {
+ int pk = permutation[k];
+ double ypk = lmDir[pk] / diagR[pk];
+ int index = pk;
+ for (int i = 0; i < k; ++i) {
+ lmDir[permutation[i]] -= ypk * jacobian[index];
+ index += cols;
+ }
+ 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;
+ int index = pj;
+ for (int i = 0; i < j; ++i) {
+ sum += jacobian[index] * work1[permutation[i]];
+ index += cols;
+ }
+ 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;
+ int index = pj;
+ for (int i = 0; i <= j; ++i) {
+ sum += jacobian[index] * qy[i];
+ index += cols;
+ }
+ 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]] -= jacobian[i * cols + 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) {
+ jacobian[i * cols + pj] = jacobian[j * cols + 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 = jacobian[k * cols + 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)
+ jacobian[k * cols + 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 = jacobian[i * cols + pk];
+ final double temp2 = cos * rik + sin * lmDiag[i];
+ lmDiag[i] = -sin * rik + cos * lmDiag[i];
+ jacobian[i * cols + pk] = temp2;
+ }
+
+ }
+ }
+
+ // store the diagonal element of s and restore
+ // the corresponding diagonal element of R
+ int index = j * cols + permutation[j];
+ lmDiag[j] = jacobian[index];
+ jacobian[index] = 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 += jacobian[i * cols + 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>
+ * @exception EstimationException if the decomposition cannot be performed
+ */
+ private void qrDecomposition() throws EstimationException {
+
+ // initializations
+ for (int k = 0; k < cols; ++k) {
+ permutation[k] = k;
+ double norm2 = 0;
+ for (int index = k; index < jacobian.length; index += cols) {
+ double akk = jacobian[index];
+ norm2 += akk * akk;
+ }
+ jacNorm[k] = FastMath.sqrt(norm2);
+ }
+
+ // transform the matrix column after column
+ for (int k = 0; k < cols; ++k) {
+
+ // select the column with the greatest norm on active components
+ int nextColumn = -1;
+ double ak2 = Double.NEGATIVE_INFINITY;
+ for (int i = k; i < cols; ++i) {
+ double norm2 = 0;
+ int iDiag = k * cols + permutation[i];
+ for (int index = iDiag; index < jacobian.length; index += cols) {
+ double aki = jacobian[index];
+ norm2 += aki * aki;
+ }
+ if (Double.isInfinite(norm2) || Double.isNaN(norm2)) {
+ throw new EstimationException(
+ LocalizedFormats.UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN,
+ rows, cols);
+ }
+ if (norm2 > ak2) {
+ nextColumn = i;
+ ak2 = norm2;
+ }
+ }
+ if (ak2 == 0) {
+ rank = k;
+ return;
+ }
+ int pk = permutation[nextColumn];
+ permutation[nextColumn] = permutation[k];
+ permutation[k] = pk;
+
+ // choose alpha such that Hk.u = alpha ek
+ int kDiag = k * cols + pk;
+ double akk = jacobian[kDiag];
+ 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;
+ jacobian[kDiag] -= alpha;
+
+ // transform the remaining columns
+ for (int dk = cols - 1 - k; dk > 0; --dk) {
+ int dkp = permutation[k + dk] - pk;
+ double gamma = 0;
+ for (int index = kDiag; index < jacobian.length; index += cols) {
+ gamma += jacobian[index] * jacobian[index + dkp];
+ }
+ gamma *= betak;
+ for (int index = kDiag; index < jacobian.length; index += cols) {
+ jacobian[index + dkp] -= gamma * jacobian[index];
+ }
+ }
+
+ }
+
+ 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) {
+ for (int k = 0; k < cols; ++k) {
+ int pk = permutation[k];
+ int kDiag = k * cols + pk;
+ double gamma = 0;
+ int index = kDiag;
+ for (int i = k; i < rows; ++i) {
+ gamma += jacobian[index] * y[i];
+ index += cols;
+ }
+ gamma *= beta[pk];
+ index = kDiag;
+ for (int i = k; i < rows; ++i) {
+ y[i] -= gamma * jacobian[index];
+ index += cols;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java b/src/main/java/org/apache/commons/math/estimation/SimpleEstimationProblem.java
new file mode 100644
index 0000000..d9449d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/SimpleEstimationProblem.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.math.estimation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Simple implementation of the {@link EstimationProblem
+ * EstimationProblem} interface for boilerplate data handling.
+ * <p>This class <em>only</em> handles parameters and measurements
+ * storage and unbound parameters filtering. It does not compute
+ * anything by itself. It should either be used with measurements
+ * implementation that are smart enough to know about the
+ * various parameters in order to compute the partial derivatives
+ * appropriately. Since the problem-specific logic is mainly related to
+ * the various measurements models, the simplest way to use this class
+ * is by extending it and using one internal class extending
+ * {@link WeightedMeasurement WeightedMeasurement} for each measurement
+ * type. The instances of the internal classes would have access to the
+ * various parameters and their current estimate.</p>
+
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+
+ */
+@Deprecated
+public class SimpleEstimationProblem implements EstimationProblem {
+
+ /** Estimated parameters. */
+ private final List<EstimatedParameter> parameters;
+
+ /** Measurements. */
+ private final List<WeightedMeasurement> measurements;
+
+ /**
+ * Build an empty instance without parameters nor measurements.
+ */
+ public SimpleEstimationProblem() {
+ parameters = new ArrayList<EstimatedParameter>();
+ measurements = new ArrayList<WeightedMeasurement>();
+ }
+
+ /**
+ * Get all the parameters of the problem.
+ * @return parameters
+ */
+ public EstimatedParameter[] getAllParameters() {
+ return parameters.toArray(new EstimatedParameter[parameters.size()]);
+ }
+
+ /**
+ * Get the unbound parameters of the problem.
+ * @return unbound parameters
+ */
+ public EstimatedParameter[] getUnboundParameters() {
+
+ // filter the unbound parameters
+ List<EstimatedParameter> unbound = new ArrayList<EstimatedParameter>(parameters.size());
+ for (EstimatedParameter p : parameters) {
+ if (! p.isBound()) {
+ unbound.add(p);
+ }
+ }
+
+ // convert to an array
+ return unbound.toArray(new EstimatedParameter[unbound.size()]);
+
+ }
+
+ /**
+ * Get the measurements of an estimation problem.
+ * @return measurements
+ */
+ public WeightedMeasurement[] getMeasurements() {
+ return measurements.toArray(new WeightedMeasurement[measurements.size()]);
+ }
+
+ /** Add a parameter to the problem.
+ * @param p parameter to add
+ */
+ protected void addParameter(EstimatedParameter p) {
+ parameters.add(p);
+ }
+
+ /**
+ * Add a new measurement to the set.
+ * @param m measurement to add
+ */
+ protected void addMeasurement(WeightedMeasurement m) {
+ measurements.add(m);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/WeightedMeasurement.java b/src/main/java/org/apache/commons/math/estimation/WeightedMeasurement.java
new file mode 100644
index 0000000..cc6ac9f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/WeightedMeasurement.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.math.estimation;
+
+import java.io.Serializable;
+
+/**
+ * This class represents measurements in estimation problems.
+ *
+ * <p>This abstract class implements all the methods needed to handle
+ * measurements in a general way. It defines neither the {@link
+ * #getTheoreticalValue getTheoreticalValue} nor the {@link
+ * #getPartial getPartial} methods, which should be defined by
+ * sub-classes according to the specific problem.</p>
+ *
+ * <p>The {@link #getTheoreticalValue getTheoreticalValue} and {@link
+ * #getPartial getPartial} methods must always use the current
+ * estimate of the parameters set by the solver in the problem. These
+ * parameters can be retrieved through the {@link
+ * EstimationProblem#getAllParameters
+ * EstimationProblem.getAllParameters} method if the measurements are
+ * independent of the problem, or directly if they are implemented as
+ * inner classes of the problem.</p>
+ *
+ * <p>The instances for which the <code>ignored</code> flag is set
+ * through the {@link #setIgnored setIgnored} method are ignored by the
+ * solvers. This can be used to reject wrong measurements at some
+ * steps of the estimation.</p>
+ *
+ * @see EstimationProblem
+ *
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @since 1.2
+ * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
+ * been deprecated and replaced by package org.apache.commons.math.optimization.general
+ */
+
+@Deprecated
+public abstract class WeightedMeasurement implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 4360046376796901941L;
+
+ /** Measurement weight. */
+ private final double weight;
+
+ /** Value of the measurements. */
+ private final double measuredValue;
+
+ /** Ignore measurement indicator. */
+ private boolean ignored;
+
+ /**
+ * Simple constructor.
+ * Build a measurement with the given parameters, and set its ignore
+ * flag to false.
+ * @param weight weight of the measurement in the least squares problem
+ * (two common choices are either to use 1.0 for all measurements, or to
+ * use a value proportional to the inverse of the variance of the measurement
+ * type)
+ *
+ * @param measuredValue measured value
+ */
+ public WeightedMeasurement(double weight, double measuredValue) {
+ this.weight = weight;
+ this.measuredValue = measuredValue;
+ ignored = false;
+ }
+
+ /** Simple constructor.
+ *
+ * Build a measurement with the given parameters
+ *
+ * @param weight weight of the measurement in the least squares problem
+ * @param measuredValue measured value
+ * @param ignored true if the measurement should be ignored
+ */
+ public WeightedMeasurement(double weight, double measuredValue,
+ boolean ignored) {
+ this.weight = weight;
+ this.measuredValue = measuredValue;
+ this.ignored = ignored;
+ }
+
+ /**
+ * Get the weight of the measurement in the least squares problem
+ *
+ * @return weight
+ */
+ public double getWeight() {
+ return weight;
+ }
+
+ /**
+ * Get the measured value
+ *
+ * @return measured value
+ */
+ public double getMeasuredValue() {
+ return measuredValue;
+ }
+
+ /**
+ * Get the residual for this measurement
+ * The residual is the measured value minus the theoretical value.
+ *
+ * @return residual
+ */
+ public double getResidual() {
+ return measuredValue - getTheoreticalValue();
+ }
+
+ /**
+ * Get the theoretical value expected for this measurement
+ * <p>The theoretical value is the value expected for this measurement
+ * if the model and its parameter were all perfectly known.</p>
+ * <p>The value must be computed using the current estimate of the parameters
+ * set by the solver in the problem.</p>
+ *
+ * @return theoretical value
+ */
+ public abstract double getTheoreticalValue();
+
+ /**
+ * Get the partial derivative of the {@link #getTheoreticalValue
+ * theoretical value} according to the parameter.
+ * <p>The value must be computed using the current estimate of the parameters
+ * set by the solver in the problem.</p>
+ *
+ * @param parameter parameter against which the partial derivative
+ * should be computed
+ * @return partial derivative of the {@link #getTheoreticalValue
+ * theoretical value}
+ */
+ public abstract double getPartial(EstimatedParameter parameter);
+
+ /**
+ * Set the ignore flag to the specified value
+ * Setting the ignore flag to true allow to reject wrong
+ * measurements, which sometimes can be detected only rather late.
+ *
+ * @param ignored value for the ignore flag
+ */
+ public void setIgnored(boolean ignored) {
+ this.ignored = ignored;
+ }
+
+ /**
+ * Check if this measurement should be ignored
+ *
+ * @return true if the measurement should be ignored
+ */
+ public boolean isIgnored() {
+ return ignored;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/estimation/package.html b/src/main/java/org/apache/commons/math/estimation/package.html
new file mode 100644
index 0000000..2f4dc05
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/estimation/package.html
@@ -0,0 +1,25 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 754732 $ -->
+<body>
+This package provided classes to solve estimation problems, it is deprecated since 2.0.
+
+<p>This package has been deprecated as of 2.0. It is replaced by the optimization.general package.</p>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/exception/ConvergenceException.java b/src/main/java/org/apache/commons/math/exception/ConvergenceException.java
new file mode 100644
index 0000000..0e1a976
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/ConvergenceException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.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
+ * @version $Revision: 1026666 $ $Date: 2010-10-23 21:30:48 +0200 (sam. 23 oct. 2010) $
+ */
+public class ConvergenceException extends MathIllegalStateException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4330003017885151975L;
+
+ /**
+ * Construct the exception.
+ */
+ public ConvergenceException() {
+ this(null);
+ }
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific contexte pattern.
+ */
+ public ConvergenceException(Localizable specific) {
+ this(specific,
+ LocalizedFormats.CONVERGENCE_FAILED,
+ null);
+ }
+ /**
+ * Construct the exception with a specific context and arguments.
+ *
+ * @param specific Specific contexte pattern.
+ * @param args Arguments.
+ */
+ public ConvergenceException(Localizable specific,
+ Object ... args) {
+ super(specific,
+ LocalizedFormats.CONVERGENCE_FAILED,
+ args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/DimensionMismatchException.java b/src/main/java/org/apache/commons/math/exception/DimensionMismatchException.java
new file mode 100644
index 0000000..d6a522d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/DimensionMismatchException.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.math.exception;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when two dimensions differ.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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 wrong Wrong dimension.
+ * @param expected Expected dimension.
+ */
+ public DimensionMismatchException(int wrong,
+ int expected) {
+ super(LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, wrong, expected);
+ dimension = expected;
+ }
+
+ /**
+ * @return the expected dimension.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathIllegalArgumentException.java b/src/main/java/org/apache/commons/math/exception/MathIllegalArgumentException.java
new file mode 100644
index 0000000..fb0ed88
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathIllegalArgumentException.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.math.exception;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.ArgUtils;
+import org.apache.commons.math.exception.util.MessageFactory;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Base class for all preconditions violation exceptions.
+ * This class is not intended to be instantiated directly: it should serve
+ * as a base class to create all the exceptions that share the semantics of
+ * the standard {@link IllegalArgumentException}, but must also provide a
+ * localized message.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class MathIllegalArgumentException extends IllegalArgumentException implements MathThrowable {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /**
+ * Pattern used to build the message (specific context).
+ */
+ private final Localizable specific;
+ /**
+ * Pattern used to build the message (general problem description).
+ */
+ private final Localizable general;
+ /**
+ * Arguments used to build the message.
+ */
+ private final Object[] arguments;
+
+ /**
+ * @param specific Message pattern providing the specific context of
+ * the error.
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ protected MathIllegalArgumentException(Localizable specific,
+ Localizable general,
+ Object ... args) {
+ this.specific = specific;
+ this.general = general;
+ arguments = ArgUtils.flatten(args);
+ }
+ /**
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ protected MathIllegalArgumentException(Localizable general,
+ Object ... args) {
+ this(null, general, args);
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getSpecificPattern() {
+ return specific;
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getGeneralPattern() {
+ return general;
+ }
+
+ /** {@inheritDoc} */
+ public Object[] getArguments() {
+ return arguments.clone();
+ }
+
+ /**
+ * Get 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 MessageFactory.buildMessage(locale, specific, general, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathIllegalNumberException.java b/src/main/java/org/apache/commons/math/exception/MathIllegalNumberException.java
new file mode 100644
index 0000000..b96ce20
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathIllegalNumberException.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.math.exception;
+
+import org.apache.commons.math.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
+ * @version $Revision$ $Date$
+ */
+public class MathIllegalNumberException extends MathIllegalArgumentException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -7447085893598031110L;
+
+ /** Requested. */
+ private final Number argument;
+
+ /**
+ * Construct an exception.
+ *
+ * @param specific Localizable pattern.
+ * @param general Localizable pattern.
+ * @param wrong wrong number
+ * @param arguments Arguments.
+ */
+ protected MathIllegalNumberException(Localizable specific,
+ Localizable general,
+ Number wrong,
+ Object ... arguments) {
+ super(specific, general, wrong, arguments);
+ argument = wrong;
+ }
+
+ /**
+ * Construct an exception.
+ *
+ * @param general Localizable pattern.
+ * @param wrong wrong number
+ * @param arguments Arguments.
+ */
+ protected MathIllegalNumberException(Localizable general,
+ Number wrong,
+ Object ... arguments) {
+ super(general, wrong, arguments);
+ argument = wrong;
+ }
+
+ /**
+ * @return the requested value.
+ */
+ public Number getArgument() {
+ return argument;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathIllegalStateException.java b/src/main/java/org/apache/commons/math/exception/MathIllegalStateException.java
new file mode 100644
index 0000000..d4e83d4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathIllegalStateException.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.math.exception;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.ArgUtils;
+import org.apache.commons.math.exception.util.MessageFactory;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Base class for all exceptions that signal a mismatch between the
+ * current state and the user's expectations.
+ *
+ * @since 2.2
+ * @version $Revision: 1061496 $ $Date: 2011-01-20 21:32:16 +0100 (jeu. 20 janv. 2011) $
+ */
+public class MathIllegalStateException extends IllegalStateException implements MathThrowable {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /**
+ * Pattern used to build the message (specific context).
+ */
+ private final Localizable specific;
+ /**
+ * Pattern used to build the message (general problem description).
+ */
+ private final Localizable general;
+ /**
+ * Arguments used to build the message.
+ */
+ private final Object[] arguments;
+
+ /**
+ * Simple constructor.
+ * @param specific Message pattern providing the specific context of
+ * the error.
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Localizable specific,
+ Localizable general,
+ Object ... args) {
+ this(null, specific, general, args);
+ }
+
+ /**
+ * Simple constructor.
+ * @param cause root cause
+ * @param specific Message pattern providing the specific context of
+ * the error.
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Throwable cause,
+ Localizable specific,
+ Localizable general,
+ Object ... args) {
+ super(cause);
+ this.specific = specific;
+ this.general = general;
+ arguments = ArgUtils.flatten(args);
+ }
+
+ /**
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Localizable general,
+ Object ... args) {
+ this(null, null, general, args);
+ }
+
+ /**
+ * Simple constructor.
+ * @param cause root cause
+ * @param general Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Throwable cause,
+ Localizable general,
+ Object ... args) {
+ this(cause, null, general, args);
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getSpecificPattern() {
+ return specific;
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getGeneralPattern() {
+ return general;
+ }
+
+ /** {@inheritDoc} */
+ public Object[] getArguments() {
+ return arguments.clone();
+ }
+
+ /**
+ * Get 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 MessageFactory.buildMessage(locale, specific, general, arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathInternalError.java b/src/main/java/org/apache/commons/math/exception/MathInternalError.java
new file mode 100644
index 0000000..623fe93
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathInternalError.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.math.exception;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception triggered when something that shouldn't happen does happen.
+ *
+ * @since 2.2
+ * @version $Revision: 1061496 $ $Date: 2011-01-20 21:32:16 +0100 (jeu. 20 janv. 2011) $
+ */
+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() {
+ super(LocalizedFormats.INTERNAL_ERROR, REPORT_URL);
+ }
+
+ /**
+ * Simple constructor.
+ * @param cause root cause
+ */
+ public MathInternalError(final Throwable cause) {
+ super(LocalizedFormats.INTERNAL_ERROR, REPORT_URL);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathThrowable.java b/src/main/java/org/apache/commons/math/exception/MathThrowable.java
new file mode 100644
index 0000000..5f47d5c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathThrowable.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.math.exception;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+* Interface for commons-math throwables.
+*
+* @version $Revision: 1035475 $ $Date: 2010-11-15 23:39:25 +0100 (lun. 15 nov. 2010) $
+* @since 2.2
+*/
+public interface MathThrowable {
+
+ /** Gets the localizable pattern used to build the specific part of the message of this throwable.
+ * @return localizable pattern used to build the specific part of the message of this throwable
+ */
+ Localizable getSpecificPattern();
+
+ /** Gets the localizable pattern used to build the general part of the message of this throwable.
+ * @return localizable pattern used to build the general part of the message of this throwable
+ */
+ Localizable getGeneralPattern();
+
+ /** Gets the arguments used to build the message of this throwable.
+ * @return the arguments used to build the message of this throwable
+ */
+ Object[] getArguments();
+
+ /** Gets the message in a specified locale.
+ * @param locale Locale in which the message should be translated
+ * @return localized message
+ */
+ String getMessage(final Locale locale);
+
+ /** Gets the message in a conventional US locale.
+ * @return localized message
+ */
+ String getMessage();
+
+ /** Gets the message in the system default locale.
+ * @return localized message
+ */
+ String getLocalizedMessage();
+
+}
diff --git a/src/main/java/org/apache/commons/math/exception/MathUnsupportedOperationException.java b/src/main/java/org/apache/commons/math/exception/MathUnsupportedOperationException.java
new file mode 100644
index 0000000..592aa37
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/MathUnsupportedOperationException.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.math.exception;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.ArgUtils;
+import org.apache.commons.math.exception.util.MessageFactory;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Base class for all unsupported features.
+ * It is used for all the exceptions that share the semantics of the standard
+ * {@link UnsupportedOperationException}, but must also provide a localized
+ * message.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class MathUnsupportedOperationException extends UnsupportedOperationException implements MathThrowable {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /**
+ * Pattern used to build the message (specific context).
+ */
+ private final Localizable specific;
+ /**
+ * Arguments used to build the message.
+ */
+ private final Object[] arguments;
+
+ /**
+ * @param args Arguments.
+ */
+ public MathUnsupportedOperationException(Object ... args) {
+ this(null, args);
+ }
+ /**
+ * @param specific Message pattern providing the specific context of
+ * the error.
+ * @param args Arguments.
+ */
+ public MathUnsupportedOperationException(Localizable specific,
+ Object ... args) {
+ this.specific = specific;
+ arguments = ArgUtils.flatten(args);
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getSpecificPattern() {
+ return specific;
+ }
+
+ /** {@inheritDoc} */
+ public Localizable getGeneralPattern() {
+ return LocalizedFormats.UNSUPPORTED_OPERATION;
+ }
+
+ /** {@inheritDoc} */
+ public Object[] getArguments() {
+ return arguments.clone();
+ }
+
+ /**
+ * Get 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 MessageFactory.buildMessage(locale,
+ specific,
+ LocalizedFormats.UNSUPPORTED_OPERATION,
+ arguments);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/NoDataException.java b/src/main/java/org/apache/commons/math/exception/NoDataException.java
new file mode 100644
index 0000000..c1b4d56
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NoDataException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when the required data is missing.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class NoDataException extends MathIllegalStateException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -3629324471511904459L;
+
+ /**
+ * Construct the exception.
+ */
+ public NoDataException() {
+ this(null);
+ }
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Contextual information on what caused the exception.
+ */
+ public NoDataException(Localizable specific) {
+ super(specific, LocalizedFormats.NO_DATA, (Object[]) null);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/NonMonotonousSequenceException.java b/src/main/java/org/apache/commons/math/exception/NonMonotonousSequenceException.java
new file mode 100644
index 0000000..3ad11ec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NonMonotonousSequenceException.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.exception;
+
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when the a sequence of values is not monotonously
+ * increasing or decreasing.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class NonMonotonousSequenceException extends MathIllegalNumberException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 3596849179428944575L;
+
+ /**
+ * Direction (positive for increasing, negative for decreasing).
+ */
+ private final MathUtils.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 NonMonotonousSequenceException(Number wrong,
+ Number previous,
+ int index) {
+ this(wrong, previous, index, MathUtils.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 NonMonotonousSequenceException(Number wrong,
+ Number previous,
+ int index,
+ MathUtils.OrderDirection direction,
+ boolean strict) {
+ super(direction == MathUtils.OrderDirection.INCREASING ?
+ (strict ?
+ LocalizedFormats.NOT_STRICTLY_INCREASING_SEQUENCE :
+ LocalizedFormats.NOT_INCREASING_SEQUENCE) :
+ (strict ?
+ LocalizedFormats.NOT_STRICTLY_DECREASING_SEQUENCE :
+ LocalizedFormats.NOT_DECREASING_SEQUENCE),
+ wrong, previous, index, index - 1);
+
+ this.direction = direction;
+ this.strict = strict;
+ this.index = index;
+ this.previous = previous;
+ }
+
+ /**
+ * @return the order direction.
+ **/
+ public MathUtils.OrderDirection getDirection() {
+ return direction;
+ }
+ /**
+ * @return {@code true} is the sequence should be strictly monotonous.
+ **/
+ 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/math/exception/NotPositiveException.java b/src/main/java/org/apache/commons/math/exception/NotPositiveException.java
new file mode 100644
index 0000000..4af597a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NotPositiveException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Exception to be thrown when the argument is negative.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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, 0, 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, 0, true);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/NotStrictlyPositiveException.java b/src/main/java/org/apache/commons/math/exception/NotStrictlyPositiveException.java
new file mode 100644
index 0000000..4b96b1d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NotStrictlyPositiveException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Exception to be thrown when the argument is negative.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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, 0, 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, 0, false);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/NullArgumentException.java b/src/main/java/org/apache/commons/math/exception/NullArgumentException.java
new file mode 100644
index 0000000..e06d25f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NullArgumentException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.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}.
+ * Proagation of {@code NullPointerException} from within Commons-Math is
+ * construed to be a bug.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class NullArgumentException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /**
+ * Default constructor.
+ */
+ public NullArgumentException() {
+ super(LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ /**
+ * @param specific Message pattern providing the specific context of
+ * the error.
+ */
+ public NullArgumentException(Localizable specific) {
+ super(specific, LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/NumberIsTooLargeException.java b/src/main/java/org/apache/commons/math/exception/NumberIsTooLargeException.java
new file mode 100644
index 0000000..9e1559b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NumberIsTooLargeException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is too large.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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(null, wrong, max, boundIsAllowed);
+ }
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific contexte 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,
+ boundIsAllowed ?
+ LocalizedFormats.NUMBER_TOO_LARGE :
+ LocalizedFormats.NUMBER_TOO_LARGE_BOUND_EXCLUDED,
+ 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/math/exception/NumberIsTooSmallException.java b/src/main/java/org/apache/commons/math/exception/NumberIsTooSmallException.java
new file mode 100644
index 0000000..349ffd1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/NumberIsTooSmallException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is too small.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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(null, wrong, min, boundIsAllowed);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific contexte 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,
+ boundIsAllowed ?
+ LocalizedFormats.NUMBER_TOO_SMALL :
+ LocalizedFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
+ 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/math/exception/OutOfRangeException.java b/src/main/java/org/apache/commons/math/exception/OutOfRangeException.java
new file mode 100644
index 0000000..268fcb5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/OutOfRangeException.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.math.exception;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when some argument is out of range.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+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) {
+ super(LocalizedFormats.OUT_OF_RANGE_SIMPLE, 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/math/exception/ZeroException.java b/src/main/java/org/apache/commons/math/exception/ZeroException.java
new file mode 100644
index 0000000..bd14365
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/ZeroException.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.math.exception;
+
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when zero is provided where it is not allowed.
+ *
+ * @since 2.2
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class ZeroException extends MathIllegalNumberException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1960874856936000015L;
+
+ /**
+ * Construct the exception.
+ */
+ public ZeroException() {
+ this(null);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific contexte pattern .
+ */
+ public ZeroException(Localizable specific) {
+ super(specific, LocalizedFormats.ZERO_NOT_ALLOWED, 0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/package.html b/src/main/java/org/apache/commons/math/exception/package.html
new file mode 100644
index 0000000..301f866
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/package.html
@@ -0,0 +1,23 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 981404 $ $Date: 2010-08-02 10:10:39 +0200 (lun. 02 août 2010) $ -->
+ <body>
+ Specialized exceptions for algorithms errors. The exceptions can be localized
+ using simple java properties.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/exception/util/ArgUtils.java b/src/main/java/org/apache/commons/math/exception/util/ArgUtils.java
new file mode 100644
index 0000000..d91134c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/util/ArgUtils.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.math.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Utility class for transforming the list of arguments passed to
+ * constructors of exceptions.
+ *
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public class ArgUtils {
+ /**
+ * Private constructor
+ */
+ 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/math/exception/util/DummyLocalizable.java b/src/main/java/org/apache/commons/math/exception/util/DummyLocalizable.java
new file mode 100644
index 0000000..449ae75
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/util/DummyLocalizable.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.math.exception.util;
+
+import java.util.Locale;
+
+/**
+ * Dummy implementation of the {@link Localizable} interface, without localization.
+ *
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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/math/exception/util/Localizable.java b/src/main/java/org/apache/commons/math/exception/util/Localizable.java
new file mode 100644
index 0000000..47efe91
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.exception.util;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+/**
+ * Interface for localizable strings.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.2
+ */
+public interface Localizable extends Serializable {
+
+ /**
+ * Get the source (non-localized) string.
+ * @return source string
+ */
+ String getSourceString();
+
+ /**
+ * Get the localized string.
+ * @param locale locale into which to get the string
+ * @return 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/math/exception/util/LocalizedFormats.java b/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.java
new file mode 100644
index 0000000..5c417f3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/util/LocalizedFormats.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.math.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
+ * @version $Revision: 1073165 $ $Date: 2011-02-21 23:04:14 +0100 (lun. 21 févr. 2011) $
+ */
+public enum LocalizedFormats implements Localizable {
+
+ // CHECKSTYLE: stop MultipleVariableDeclarations
+ // CHECKSTYLE: stop JavadocVariable
+
+ ARGUMENT_OUTSIDE_DOMAIN("Argument {0} outside domain [{1} ; {2}]"),
+ 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_OUT_OF_INTERVAL("bandwidth must be in the interval [0,1], but got {0}"),
+ BINOMIAL_INVALID_PARAMETERS_ORDER("must have n >= k for binomial coefficient (n,k), got n = {0}, k = {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_CONVERT_OBJECT_TO_FRACTION("cannot convert given object to a fraction number: {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"),
+ 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}]"),
+ 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"),
+ 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}"),
+ DIGEST_NOT_INITIALIZED("digest not initialized"),
+ DIMENSIONS_MISMATCH_2x2("dimensions mismatch: got {0}x{1} but expected {2}x{3}"),
+ DIMENSIONS_MISMATCH_SIMPLE("dimensions mismatch {0} != {1}"), /* keep */
+ DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN("Discrete cumulative probability function returned NaN for argument {0}"),
+ DISTRIBUTION_NOT_LOADED("distribution not loaded"),
+ DUPLICATED_ABSCISSA("Abscissa {0} is duplicated at both indices {1} and {2}"),
+ EMPTY_CLUSTER_IN_K_MEANS("empty cluster in k-means"),
+ 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_FAILED("evaluation failed for argument = {0}"),
+ EXPANSION_FACTOR_SMALLER_THAN_ONE("expansion factor smaller than one ({0})"),
+ FACTORIAL_NEGATIVE_PARAMETER("must have n >= 0 for n!, got n = {0}"),
+ FAILED_BRACKETING("number of iterations={0}, maximum iterations={1}, initial={2}, lower bound={3}, upper bound={4}, final a value={5}, final b value={6}, f(a)={7}, f(b)={8}"),
+ 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"),
+ IDENTICAL_ABSCISSAS_DIVISION_BY_ZERO("identical abscissas x[{0}] == x[{1}] == {2} cause division by zero"),
+ 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}]"),
+ 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 {0} after final column {1}"),
+ INITIAL_ROW_AFTER_FINAL_ROW("initial row {0} after final row {1}"),
+ 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_FOR_T_STATISTIC("insufficient data for t statistic, needs at least 2, got {0}"),
+ INSUFFICIENT_DIMENSION("insufficient dimension {0}, must be at least {1}"),
+ 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_ONE_PREVIOUS_POINT("{0} method needs at least one previous point"),
+ INTERNAL_ERROR("internal error, please fill a bug report at {0}"),
+ INVALID_BRACKETING_PARAMETERS("invalid bracketing parameters: lower bound={0}, initial={1}, upper bound={2}"),
+ 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}"),
+ INVALID_REGRESSION_ARRAY("input data array length = {0} does not match the number of observations = {1} and the number of regressors = {2}"),
+ 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"),
+ 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"),
+ MAX_EVALUATIONS_EXCEEDED("maximal number of evaluations ({0}) exceeded"),
+ MAX_ITERATIONS_EXCEEDED("maximal number of iterations ({0}) exceeded"),
+ MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION("minimal step size ({0,number,0.00E00}) reached, integration needs {1,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"),
+ 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})"),
+ NEGATIVE_NUMBER_OF_TRIALS("number of trials must be non-negative ({0})"),
+ NEGATIVE_ROBUSTNESS_ITERATIONS("the number of robustness iterations must be non-negative, but got {0}"),
+ START_POSITION("start position ({0})"), /* keep */
+ NON_CONVERGENT_CONTINUED_FRACTION("Continued fraction convergents failed to converge for value {0}"),
+ 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("a {0}x{1} matrix was provided instead of a square matrix"),
+ 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_OVERRIDEN("method not overriden"),
+ NOT_POSITIVE_ALPHA("alpha must be positive ({0})"),
+ NOT_POSITIVE_BETA("beta must be positive ({0})"),
+ NOT_POSITIVE_COLUMNDIMENSION("invalid column dimension: {0} (must be positive)"),
+ NOT_POSITIVE_DEFINITE_MATRIX("not positive definite matrix"),
+ 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)"),
+ 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})"),
+ 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})"),
+ NOT_POSITIVE_SHAPE("shape must be positive ({0})"),
+ 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_SYMMETRIC_MATRIX("not symmetric matrix"),
+ NO_BIN_SELECTED("no bin selected"),
+ NO_CONVERGENCE_WITH_ANY_START_POINT("none of the {0} start points lead to convergence"),
+ 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"),
+ NO_RESULT_AVAILABLE("no result available"),
+ NO_SUCH_MATRIX_ENTRY("no entry at indices ({0}, {1}) in a {2}x{3} matrix"),
+ NULL_NOT_ALLOWED("null is not allowed"), /* keep */
+ 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}"),
+ OUT_OF_BOUNDS_QUANTILE_VALUE("out of bounds quantile value: {0}, must be in (0, 100]"),
+ OUT_OF_BOUND_SIGNIFICANCE_LEVEL("out of bounds significance level {0}, must be between {1} and {2}"),
+ 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_RANGE_ROOT_OF_UNITY_INDEX("out of range root of unity index {0} (must be in [{1};{2}])"),
+ OUT_OF_RANGE_SIMPLE("{0} out of [{1}, {2}] range"), /* 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}"),
+ 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_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"),
+ POSITION_SIZE_MISMATCH_INPUT_ARRAY("position {0} and size {1} don't fit to the size of the input array {2}"),
+ 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}]"),
+ 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"),
+ 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_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY("cannot discard {0} elements from a {1} elements array"),
+ TOO_SMALL_BANDWIDTH("the bandwidth must be large enough to accomodate at least 2 points. There are {0} data points, and bandwidth must be at least {1} but it is only {2}"),
+ 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"),
+ 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})"),
+ UNPARSEABLE_3D_VECTOR("unparseable 3D vector: \"{0}\""),
+ UNPARSEABLE_COMPLEX_NUMBER("unparseable complex number: \"{0}\""),
+ UNPARSEABLE_FRACTION_NUMBER("unparseable fraction 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 */
+ 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"),
+ 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
+ */
+ private LocalizedFormats(final String sourceFormat) {
+ this.sourceFormat = sourceFormat;
+ }
+
+ /** {@inheritDoc} */
+ public String getSourceString() {
+ return sourceFormat;
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalizedString(final Locale locale) {
+ try {
+ ResourceBundle bundle =
+ ResourceBundle.getBundle("META-INF/localization/LocalizedFormats", locale);
+ if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
+ // the value of the resource is the translated format
+ return bundle.getString(toString());
+ }
+
+ } catch (MissingResourceException mre) {
+ // 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/math/exception/util/MessageFactory.java b/src/main/java/org/apache/commons/math/exception/util/MessageFactory.java
new file mode 100644
index 0000000..0e8cf14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/util/MessageFactory.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.math.exception.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+
+/**
+ * Class for constructing localized messages.
+ *
+ * @since 2.2
+ * @version $Revision$ $Date$
+ */
+public class MessageFactory {
+ /**
+ * Class contains only static methods.
+ */
+ private MessageFactory() {}
+
+ /**
+ * Builds a message string by from a pattern and its arguments.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param pattern Format specifier.
+ * @param arguments Format arguments.
+ * @return a localized message string.
+ */
+ public static String buildMessage(Locale locale,
+ Localizable pattern,
+ Object ... arguments) {
+ return buildMessage(locale, null, pattern, arguments);
+ }
+
+ /**
+ * Builds a message string by from two patterns (specific and general) and
+ * an argument list.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param specific Format specifier (may be null).
+ * @param general Format specifier (may be null).
+ * @param arguments Format arguments. They will be substituted in
+ * <em>both</em> the {@code general} and {@code specific} format specifiers.
+ * @return a localized message string.
+ */
+ public static String buildMessage(Locale locale,
+ Localizable specific,
+ Localizable general,
+ Object ... arguments) {
+ final StringBuilder sb = new StringBuilder();
+ if (general != null) {
+ final MessageFormat fmt = new MessageFormat(general.getLocalizedString(locale), locale);
+ sb.append(fmt.format(arguments));
+ }
+ if (specific != null) {
+ if (general != null) {
+ sb.append(": ");
+ }
+ final MessageFormat fmt = new MessageFormat(specific.getLocalizedString(locale), locale);
+ sb.append(fmt.format(arguments));
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/exception/util/package.html b/src/main/java/org/apache/commons/math/exception/util/package.html
new file mode 100644
index 0000000..456a88a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/exception/util/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 1054315 $ $Date: 2011-01-02 00:22:24 +0100 (dim. 02 janv. 2011) $ -->
+ <body>
+ Classes supporting exception localization.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/fraction/AbstractFormat.java b/src/main/java/org/apache/commons/math/fraction/AbstractFormat.java
new file mode 100644
index 0000000..c409702
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/AbstractFormat.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.math.fraction;
+
+import java.io.Serializable;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Common part shared by both {@link FractionFormat} and {@link BigFractionFormat}.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @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. */
+ protected NumberFormat denominatorFormat;
+
+ /** The format used for the numerator. */
+ protected 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)} with the only
+ * customizing 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)} with the only
+ * customizing 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/ouput 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/ouput 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/math/fraction/BigFraction.java b/src/main/java/org/apache/commons/math/fraction/BigFraction.java
new file mode 100644
index 0000000..98fa676
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/BigFraction.java
@@ -0,0 +1,1129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.fraction;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Representation of a rational number without any overflow. This class is
+ * immutable.
+ *
+ * @version $Revision: 1073687 $ $Date: 2011-02-23 11:39:25 +0100 (mer. 23 févr. 2011) $
+ * @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_DOUBLE = BigInteger.valueOf(100);
+
+ /** The numerator. */
+ private final BigInteger numerator;
+
+ /** The denominator. */
+ private final BigInteger denominator;
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed <tt>BigInteger</tt>, ie
+ * "num / 1".
+ * </p>
+ *
+ * @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 ArithmeticException if the denominator is zero.
+ */
+ public BigFraction(BigInteger num, BigInteger den) {
+ if (num == null) {
+ throw new NullPointerException(LocalizedFormats.NUMERATOR.getSourceString());
+ }
+ if (den == null) {
+ throw new NullPointerException(LocalizedFormats.DENOMINATOR.getSourceString());
+ }
+ if (BigInteger.ZERO.equals(den)) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (BigInteger.ZERO.equals(num)) {
+ 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 (BigInteger.ZERO.compareTo(den) > 0) {
+ 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 does work for all values except NaN and infinities and does
+ * not requires any loop or convergence threshold.
+ * </p>
+ * <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).
+ * </p>
+ * @see #BigFraction(double, double, int)
+ * @param value the double value to convert to a fraction.
+ * @exception IllegalArgumentException if value is NaN or infinite
+ */
+ public BigFraction(final double value) throws IllegalArgumentException {
+ if (Double.isNaN(value)) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.NAN_VALUE_CONVERSION);
+ }
+ if (Double.isInfinite(value)) {
+ throw MathRuntimeException.createIllegalArgumentException(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 = 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)</li>
+ * </ul>
+ * </p>
+ *
+ * @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>
+ * <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.
+ * </p>
+ *
+ * See JIRA issue ticket MATH-181 for more details:
+ *
+ * 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 (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)) {
+ 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)</li>
+ * </ul>
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed <tt>int</tt>, ie
+ * "num / 1".
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ */
+ public BigFraction(final int num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} given the numerator and denominator as simple
+ * <tt>int</tt>. The {@link BigFraction} is reduced to lowest terms.
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ * @param den
+ * the denominator.
+ */
+ public BigFraction(final int num, final int den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed long, ie "num / 1".
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ */
+ public BigFraction(final long num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} given the numerator and denominator as simple
+ * <tt>long</tt>. The {@link BigFraction} is reduced to lowest terms.
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ * @param den
+ * the denominator.
+ */
+ public BigFraction(final long num, final long den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * <p>
+ * Creates a <code>BigFraction</code> instance with the 2 parts of a fraction
+ * Y/Z.
+ * </p>
+ *
+ * <p>
+ * Any negative signs are resolved to be on the numerator.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Returns the absolute value of this {@link BigFraction}.
+ * </p>
+ *
+ * @return the absolute value as a {@link BigFraction}.
+ */
+ public BigFraction abs() {
+ return (BigInteger.ZERO.compareTo(numerator) <= 0) ? this : negate();
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed {@link BigInteger},
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param bg
+ * the {@link BigInteger} to add, must'nt be <code>null</code>.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ * @throws NullPointerException
+ * if the {@link BigInteger} is <code>null</code>.
+ */
+ public BigFraction add(final BigInteger bg) {
+ return new BigFraction(numerator.add(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed <tt>integer</tt>, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @param i
+ * the <tt>integer</tt> to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final int i) {
+ return add(BigInteger.valueOf(i));
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed <tt>long</tt>, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @param l
+ * the <tt>long</tt> to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final long l) {
+ return add(BigInteger.valueOf(l));
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction
+ * the {@link BigFraction} to add, must not be <code>null</code>.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullPointerException if the {@link BigFraction} is {@code null}.
+ */
+ public BigFraction add(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(LocalizedFormats.FRACTION.getSourceString());
+ }
+ if (ZERO.equals(fraction)) {
+ return this;
+ }
+
+ 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);
+ }
+ return new BigFraction(num, den);
+
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <code>BigDecimal</code>. This calculates the
+ * fraction as the numerator divided by denominator.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <code>BigDecimal</code> following the passed
+ * rounding mode. This calculates the fraction as the numerator divided by
+ * denominator.
+ * </p>
+ *
+ * @param roundingMode
+ * rounding mode to apply. see {@link BigDecimal} constants.
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @throws IllegalArgumentException
+ * if <tt>roundingMode</tt> does not represent a valid rounding
+ * mode.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue(final int roundingMode) {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator), roundingMode);
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Compares this object to another based on size.
+ * </p>
+ *
+ * @param object
+ * the object to compare to, must not be <code>null</code>.
+ * @return -1 if this is less than <tt>object</tt>, +1 if this is greater
+ * than <tt>object</tt>, 0 if they are equal.
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(final BigFraction object) {
+ BigInteger nOd = numerator.multiply(object.denominator);
+ BigInteger dOn = denominator.multiply(object.numerator);
+ return nOd.compareTo(dOn);
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed <code>BigInteger</code>,
+ * ie "this * 1 / bg", returning the result in reduced form.
+ * </p>
+ *
+ * @param bg
+ * the <code>BigInteger</code> to divide by, must not be
+ * <code>null</code>.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullPointerException if the {@code BigInteger} is {@code null}.
+ * @throws ArithmeticException
+ * if the fraction to divide by is zero.
+ */
+ public BigFraction divide(final BigInteger bg) {
+ if (BigInteger.ZERO.equals(bg)) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ return new BigFraction(numerator, denominator.multiply(bg));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed <tt>int</tt>, ie
+ * "this * 1 / i", returning the result in reduced form.
+ * </p>
+ *
+ * @param i
+ * the <tt>int</tt> to divide by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws ArithmeticException
+ * if the fraction to divide by is zero.
+ */
+ public BigFraction divide(final int i) {
+ return divide(BigInteger.valueOf(i));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed <tt>long</tt>, ie
+ * "this * 1 / l", returning the result in reduced form.
+ * </p>
+ *
+ * @param l
+ * the <tt>long</tt> to divide by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws ArithmeticException
+ * if the fraction to divide by is zero.
+ */
+ public BigFraction divide(final long l) {
+ return divide(BigInteger.valueOf(l));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction Fraction to divide by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullPointerException if the {@code fraction} is {@code null}.
+ * @throws ArithmeticException if the fraction to divide by is zero.
+ */
+ public BigFraction divide(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(LocalizedFormats.FRACTION.getSourceString());
+ }
+ if (BigInteger.ZERO.equals(fraction.numerator)) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <tt>double</tt>. This calculates the fraction as
+ * the numerator divided by denominator.
+ * </p>
+ *
+ * @return the fraction as a <tt>double</tt>
+ * @see java.lang.Number#doubleValue()
+ */
+ @Override
+ public double doubleValue() {
+ return numerator.doubleValue() / denominator.doubleValue();
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <tt>float</tt>. This calculates the fraction as
+ * the numerator divided by denominator.
+ * </p>
+ *
+ * @return the fraction as a <tt>float</tt>.
+ * @see java.lang.Number#floatValue()
+ */
+ @Override
+ public float floatValue() {
+ return numerator.floatValue() / denominator.floatValue();
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a <code>BigInteger</code>.
+ * </p>
+ *
+ * @return the denominator as a <code>BigInteger</code>.
+ */
+ public BigInteger getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a <tt>int</tt>.
+ * </p>
+ *
+ * @return the denominator as a <tt>int</tt>.
+ */
+ public int getDenominatorAsInt() {
+ return denominator.intValue();
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a <tt>long</tt>.
+ * </p>
+ *
+ * @return the denominator as a <tt>long</tt>.
+ */
+ public long getDenominatorAsLong() {
+ return denominator.longValue();
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a <code>BigInteger</code>.
+ * </p>
+ *
+ * @return the numerator as a <code>BigInteger</code>.
+ */
+ public BigInteger getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a <tt>int</tt>.
+ * </p>
+ *
+ * @return the numerator as a <tt>int</tt>.
+ */
+ public int getNumeratorAsInt() {
+ return numerator.intValue();
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a <tt>long</tt>.
+ * </p>
+ *
+ * @return the numerator as a <tt>long</tt>.
+ */
+ public long getNumeratorAsLong() {
+ return numerator.longValue();
+ }
+
+ /**
+ * <p>
+ * Gets a hashCode for the fraction.
+ * </p>
+ *
+ * @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();
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as an <tt>int</tt>. This returns the whole number part
+ * of the fraction.
+ * </p>
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#intValue()
+ */
+ @Override
+ public int intValue() {
+ return numerator.divide(denominator).intValue();
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <tt>long</tt>. This returns the whole number part
+ * of the fraction.
+ * </p>
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#longValue()
+ */
+ @Override
+ public long longValue() {
+ return numerator.divide(denominator).longValue();
+ }
+
+ /**
+ * <p>
+ * Multiplies the value of this fraction by the passed
+ * <code>BigInteger</code>, returning the result in reduced form.
+ * </p>
+ *
+ * @param bg the {@code BigInteger} to multiply by.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullPointerException if {@code bg} is {@code null}.
+ */
+ public BigFraction multiply(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullPointerException();
+ }
+ return new BigFraction(bg.multiply(numerator), denominator);
+ }
+
+ /**
+ * <p>
+ * Multiply the value of this fraction by the passed <tt>int</tt>, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @param i
+ * the <tt>int</tt> to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final int i) {
+ return multiply(BigInteger.valueOf(i));
+ }
+
+ /**
+ * <p>
+ * Multiply the value of this fraction by the passed <tt>long</tt>,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param l
+ * the <tt>long</tt> to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final long l) {
+ return multiply(BigInteger.valueOf(l));
+ }
+
+ /**
+ * <p>
+ * Multiplies the value of this fraction by another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction Fraction to multiply by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullPointerException if {@code fraction} is {@code null}.
+ */
+ public BigFraction multiply(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(LocalizedFormats.FRACTION.getSourceString());
+ }
+ if (numerator.equals(BigInteger.ZERO) ||
+ fraction.numerator.equals(BigInteger.ZERO)) {
+ return ZERO;
+ }
+ return new BigFraction(numerator.multiply(fraction.numerator),
+ denominator.multiply(fraction.denominator));
+ }
+
+ /**
+ * <p>
+ * Return the additive inverse of this fraction, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @return the negation of this fraction.
+ */
+ public BigFraction negate() {
+ return new BigFraction(numerator.negate(), denominator);
+ }
+
+ /**
+ * <p>
+ * Gets the fraction percentage as a <tt>double</tt>. This calculates the
+ * fraction as the numerator divided by denominator multiplied by 100.
+ * </p>
+ *
+ * @return the fraction percentage as a <tt>double</tt>.
+ */
+ public double percentageValue() {
+ return (numerator.divide(denominator)).multiply(ONE_HUNDRED_DOUBLE).doubleValue();
+ }
+
+ /**
+ * <p>
+ * Returns a <tt>integer</tt> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @param exponent
+ * exponent to which this <code>BigInteger</code> is to be
+ * raised.
+ * @return <tt>this<sup>exponent</sup></tt>.
+ */
+ public BigFraction pow(final int exponent) {
+ if (exponent < 0) {
+ return new BigFraction(denominator.pow(-exponent), numerator.pow(-exponent));
+ }
+ return new BigFraction(numerator.pow(exponent), denominator.pow(exponent));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>BigFraction</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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 new BigFraction(MathUtils.pow(denominator, -exponent),
+ MathUtils.pow(numerator, -exponent));
+ }
+ return new BigFraction(MathUtils.pow(numerator, exponent),
+ MathUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>BigFraction</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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.compareTo(BigInteger.ZERO) < 0) {
+ final BigInteger eNeg = exponent.negate();
+ return new BigFraction(MathUtils.pow(denominator, eNeg),
+ MathUtils.pow(numerator, eNeg));
+ }
+ return new BigFraction(MathUtils.pow(numerator, exponent),
+ MathUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>double</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Return the multiplicative inverse of this fraction.
+ * </p>
+ *
+ * @return the reciprocal fraction.
+ */
+ public BigFraction reciprocal() {
+ return new BigFraction(denominator, numerator);
+ }
+
+ /**
+ * <p>
+ * Reduce this <code>BigFraction</code> to its lowest terms.
+ * </p>
+ *
+ * @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);
+ return new BigFraction(numerator.divide(gcd), denominator.divide(gcd));
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of an {@link BigInteger} from the value of this one,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param bg the {@link BigInteger} to subtract, cannot be {@code null}.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullPointerException if the {@link BigInteger} is {@code null}.
+ */
+ public BigFraction subtract(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullPointerException();
+ }
+ return new BigFraction(numerator.subtract(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of an <tt>integer</tt> from the value of this one,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param i
+ * the <tt>integer</tt> to subtract.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction subtract(final int i) {
+ return subtract(BigInteger.valueOf(i));
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of an <tt>integer</tt> from the value of this one,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param l
+ * the <tt>long</tt> to subtract.
+ * @return a <code>BigFraction</code> instance with the resulting values, or
+ * this object if the <tt>long</tt> is zero.
+ */
+ public BigFraction subtract(final long l) {
+ return subtract(BigInteger.valueOf(l));
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of another fraction from the value of this one,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param fraction {@link BigFraction} to subtract, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws NullPointerException if the {@code fraction} is {@code null}.
+ */
+ public BigFraction subtract(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(LocalizedFormats.FRACTION.getSourceString());
+ }
+ if (ZERO.equals(fraction)) {
+ return this;
+ }
+
+ 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);
+
+ }
+
+ /**
+ * <p>
+ * Returns the <code>String</code> representing this fraction, ie
+ * "num / dem" or just "num" if the denominator is one.
+ * </p>
+ *
+ * @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/math/fraction/BigFractionField.java b/src/main/java/org/apache/commons/math/fraction/BigFractionField.java
new file mode 100644
index 0000000..bc3a7e9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/BigFractionField.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.math.fraction;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+
+/**
+ * Representation of the fractional numbers without any overflow field.
+ * <p>
+ * This class is a singleton.
+ * </p>
+ * @see Fraction
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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;
+ }
+
+ // 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 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/math/fraction/BigFractionFormat.java b/src/main/java/org/apache/commons/math/fraction/BigFractionFormat.java
new file mode 100644
index 0000000..918e5e1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/BigFractionFormat.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.fraction;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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.
+ * </p>
+ *
+ * @since 2.0
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+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 IllegalArgumentException is <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 MathRuntimeException.createIllegalArgumentException(
+ 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 ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ @Override
+ public BigFraction parse(final String source) throws ParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final BigFraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw MathRuntimeException.createParseException(
+ parsePosition.getErrorIndex(),
+ LocalizedFormats.UNPARSEABLE_FRACTION_NUMBER, source);
+ }
+ 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/ouput 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/ouput 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/math/fraction/Fraction.java b/src/main/java/org/apache/commons/math/fraction/Fraction.java
new file mode 100644
index 0000000..82ecf63
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/Fraction.java
@@ -0,0 +1,655 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.fraction;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Representation of a rational number.
+ *
+ * implements Serializable since 2.0
+ *
+ * @since 1.1
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+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 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, 1.0e-5, 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)</li>
+ * </ul>
+ * </p>
+ * @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.
+ */
+ 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)</li>
+ * </ul>
+ * </p>
+ * @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><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.
+ * </p>
+ *
+ * See JIRA issue ticket MATH-181 for more details:
+ *
+ * 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 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 (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 ((p2 > overflow) || (q2 > overflow)) {
+ 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 ArithmeticException if the denominator is <code>zero</code>
+ */
+ public Fraction(int num, int den) {
+ if (den == 0) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.ZERO_DENOMINATOR_IN_FRACTION, num, den);
+ }
+ if (den < 0) {
+ if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.OVERFLOW_IN_FRACTION, num, den);
+ }
+ num = -num;
+ den = -den;
+ }
+ // reduce numerator and denominator by greatest common denominator.
+ final int d = MathUtils.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 <tt>object</tt>, +1 if this is greater
+ * than <tt>object</tt>, 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 <tt>double</tt>. This calculates the fraction as
+ * the numerator divided by denominator.
+ * @return the fraction as a <tt>double</tt>
+ */
+ @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
+ * <tt>null</tt>, 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 <tt>float</tt>. This calculates the fraction as
+ * the numerator divided by denominator.
+ * @return the fraction as a <tt>float</tt>
+ */
+ @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 <tt>int</tt>. 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 <tt>long</tt>. 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 MathRuntimeException.createArithmeticException(
+ 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);
+ }
+
+ /**
+ * <p>Adds the value of this fraction to another, returning the result in reduced form.
+ * The algorithm follows Knuth, 4.5.1.</p>
+ *
+ * @param fraction the fraction to add, must not be <code>null</code>
+ * @return a <code>Fraction</code> instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is <code>null</code>
+ * @throws ArithmeticException if the resulting numerator or denominator exceeds
+ * <code>Integer.MAX_VALUE</code>
+ */
+ public Fraction add(Fraction fraction) {
+ return addSub(fraction, true /* add */);
+ }
+
+ /**
+ * Add an integer to the fraction.
+ * @param i the <tt>integer</tt> to add.
+ * @return this + i
+ */
+ public Fraction add(final int i) {
+ return new Fraction(numerator + i * denominator, denominator);
+ }
+
+ /**
+ * <p>Subtracts the value of another fraction from the value of this one,
+ * returning the result in reduced form.</p>
+ *
+ * @param fraction the fraction to subtract, must not be <code>null</code>
+ * @return a <code>Fraction</code> instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is <code>null</code>
+ * @throws ArithmeticException if the resulting numerator or denominator
+ * cannot be represented in an <code>int</code>.
+ */
+ public Fraction subtract(Fraction fraction) {
+ return addSub(fraction, false /* subtract */);
+ }
+
+ /**
+ * Subtract an integer from the fraction.
+ * @param i the <tt>integer</tt> 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</code>
+ * @param isAdd true to add, false to subtract
+ * @return a <code>Fraction</code> instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is <code>null</code>
+ * @throws ArithmeticException if the resulting numerator or denominator
+ * cannot be represented in an <code>int</code>.
+ */
+ 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 = MathUtils.gcd(denominator, fraction.denominator);
+ if (d1==1) {
+ // result is ( (u*v' +/- u'v) / u'v')
+ int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator);
+ int upv = MathUtils.mulAndCheck(fraction.numerator, denominator);
+ return new Fraction
+ (isAdd ? MathUtils.addAndCheck(uvp, upv) :
+ MathUtils.subAndCheck(uvp, upv),
+ MathUtils.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:MathUtils.gcd(tmodd1, d1);
+
+ // result is (t/d2) / (u'/d1)(v'/d2)
+ BigInteger w = t.divide(BigInteger.valueOf(d2));
+ if (w.bitLength() > 31) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.NUMERATOR_OVERFLOW_AFTER_MULTIPLY,
+ w);
+ }
+ return new Fraction (w.intValue(),
+ MathUtils.mulAndCheck(denominator/d1,
+ fraction.denominator/d2));
+ }
+
+ /**
+ * <p>Multiplies the value of this fraction by another, returning the
+ * result in reduced form.</p>
+ *
+ * @param fraction the fraction to multiply by, must not be <code>null</code>
+ * @return a <code>Fraction</code> instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is <code>null</code>
+ * @throws ArithmeticException if the resulting numerator or denominator exceeds
+ * <code>Integer.MAX_VALUE</code>
+ */
+ 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 = MathUtils.gcd(numerator, fraction.denominator);
+ int d2 = MathUtils.gcd(fraction.numerator, denominator);
+ return getReducedFraction
+ (MathUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
+ MathUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
+ }
+
+ /**
+ * Multiply the fraction by an integer.
+ * @param i the <tt>integer</tt> to multiply by.
+ * @return this * i
+ */
+ public Fraction multiply(final int i) {
+ return new Fraction(numerator * i, denominator);
+ }
+
+ /**
+ * <p>Divide the value of this fraction by another.</p>
+ *
+ * @param fraction the fraction to divide by, must not be <code>null</code>
+ * @return a <code>Fraction</code> instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is <code>null</code>
+ * @throws ArithmeticException if the fraction to divide by is zero
+ * @throws ArithmeticException if the resulting numerator or denominator exceeds
+ * <code>Integer.MAX_VALUE</code>
+ */
+ public Fraction divide(Fraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator == 0) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.ZERO_FRACTION_TO_DIVIDE_BY,
+ fraction.numerator, fraction.denominator);
+ }
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * Divide the fraction by an integer.
+ * @param i the <tt>integer</tt> to divide by.
+ * @return this * i
+ */
+ public Fraction divide(final int i) {
+ return new Fraction(numerator, denominator * i);
+ }
+
+ /**
+ * <p>Creates a <code>Fraction</code> instance with the 2 parts
+ * of a fraction Y/Z.</p>
+ *
+ * <p>Any negative signs are resolved to be on the numerator.</p>
+ *
+ * @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 Fraction getReducedFraction(int numerator, int denominator) {
+ if (denominator == 0) {
+ throw MathRuntimeException.createArithmeticException(
+ 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 MathRuntimeException.createArithmeticException(
+ LocalizedFormats.OVERFLOW_IN_FRACTION, numerator, denominator);
+ }
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+ // simplify fraction.
+ int gcd = MathUtils.gcd(numerator, denominator);
+ numerator /= gcd;
+ denominator /= gcd;
+ return new Fraction(numerator, denominator);
+ }
+
+ /**
+ * <p>
+ * Returns the <code>String</code> representing this fraction, ie
+ * "num / dem" or just "num" if the denominator is one.
+ * </p>
+ *
+ * @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/math/fraction/FractionConversionException.java b/src/main/java/org/apache/commons/math/fraction/FractionConversionException.java
new file mode 100644
index 0000000..9c99fcd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.fraction;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a double value cannot be converted to a fraction
+ * in the allowed number of iterations.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @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/math/fraction/FractionField.java b/src/main/java/org/apache/commons/math/fraction/FractionField.java
new file mode 100644
index 0000000..e6d7c47
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/FractionField.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.math.fraction;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+
+/**
+ * Representation of the fractional numbers field.
+ * <p>
+ * This class is a singleton.
+ * </p>
+ * @see Fraction
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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;
+ }
+
+ // 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 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/math/fraction/FractionFormat.java b/src/main/java/org/apache/commons/math/fraction/FractionFormat.java
new file mode 100644
index 0000000..b84f7cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/FractionFormat.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.math.fraction;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+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 IllegalArgumentException is <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(final Object obj,
+ final StringBuffer toAppendTo, final FieldPosition pos) {
+ StringBuffer ret = null;
+
+ if (obj instanceof Fraction) {
+ ret = format((Fraction) obj, toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ try {
+ ret = format(new Fraction(((Number) obj).doubleValue()),
+ toAppendTo, pos);
+ } catch (ConvergenceException ex) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_CONVERT_OBJECT_TO_FRACTION,
+ ex.getLocalizedMessage());
+ }
+ } else {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ @Override
+ public Fraction parse(final String source) throws ParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final Fraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw MathRuntimeException.createParseException(
+ parsePosition.getErrorIndex(),
+ LocalizedFormats.UNPARSEABLE_FRACTION_NUMBER, source);
+ }
+ 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/ouput 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/math/fraction/ProperBigFractionFormat.java b/src/main/java/org/apache/commons/math/fraction/ProperBigFractionFormat.java
new file mode 100644
index 0000000..398f565
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.fraction;
+
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * 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>.</p>
+ *
+ * @since 1.1
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+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>.</p>
+ *
+ * @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/math/fraction/ProperFractionFormat.java b/src/main/java/org/apache/commons/math/fraction/ProperFractionFormat.java
new file mode 100644
index 0000000..a70925d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/ProperFractionFormat.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.math.fraction;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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>.</p>
+ *
+ * @since 1.1
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+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 = num % den;
+
+ if (whole != 0) {
+ getWholeFormat().format(whole, toAppendTo, pos);
+ toAppendTo.append(' ');
+ num = Math.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>.</p>
+ *
+ * @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(((Math.abs(w) * d) + n) * MathUtils.sign(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/math/fraction/package.html b/src/main/java/org/apache/commons/math/fraction/package.html
new file mode 100644
index 0000000..201ae20
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/fraction/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Fraction number type and fraction number formatting.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/genetics/AbstractListChromosome.java b/src/main/java/org/apache/commons/math/genetics/AbstractListChromosome.java
new file mode 100644
index 0000000..c618dff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/AbstractListChromosome.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.math.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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public abstract class AbstractListChromosome<T> extends Chromosome {
+
+ /** List representing the chromosome */
+ private final List<T> representation;
+
+ /**
+ * Constructor.
+ * @param representation inner representation of the chromosome
+ */
+ public AbstractListChromosome(final List<T> representation) {
+ try {
+ checkValidity(representation);
+ } catch (InvalidRepresentationException e) {
+ throw new IllegalArgumentException(String.format("Invalid representation for %s", getClass().getSimpleName()), e);
+ }
+ this.representation = Collections.unmodifiableList(new ArrayList<T> (representation));
+ }
+
+ /**
+ * Constructor.
+ * @param representation inner representation of the chromosome
+ */
+ public AbstractListChromosome(final T[] representation) {
+ this(Arrays.asList(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.
+ *
+ * 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/math/genetics/BinaryChromosome.java b/src/main/java/org/apache/commons/math/genetics/BinaryChromosome.java
new file mode 100644
index 0000000..19dab38
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/BinaryChromosome.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.math.genetics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Chromosome represented by a vector of 0s and 1s.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public abstract class BinaryChromosome extends AbstractListChromosome<Integer> {
+
+ /**
+ * Constructor.
+ * @param representation list of {0,1} values representing the chromosome
+ */
+ public BinaryChromosome(List<Integer> representation) {
+ super(representation);
+ }
+
+ /**
+ * Constructor.
+ * @param representation array of {0,1} values representing the chromosome
+ */
+ public BinaryChromosome(Integer[] representation) {
+ super(representation);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void checkValidity(List<Integer> chromosomeRepresentation) throws InvalidRepresentationException {
+ for (int i : chromosomeRepresentation) {
+ if (i < 0 || i >1)
+ throw new InvalidRepresentationException("Elements can be only 0 or 1.");
+ }
+ }
+
+ /**
+ * 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/math/genetics/BinaryMutation.java b/src/main/java/org/apache/commons/math/genetics/BinaryMutation.java
new file mode 100644
index 0000000..f762f89
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/BinaryMutation.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.math.genetics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mutation for {@link BinaryChromosome}s. Randomly changes one gene.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public class BinaryMutation implements MutationPolicy {
+
+ /**
+ * Mutate the given chromosome. Randomly changes one gene.
+ * @param original the original chromosome.
+ * @return the mutated chromomsome.
+ */
+ public Chromosome mutate(Chromosome original) {
+ if (!(original instanceof BinaryChromosome)) {
+ throw new IllegalArgumentException("Binary mutation works on BinaryChromosome only.");
+ }
+
+ 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/math/genetics/Chromosome.java b/src/main/java/org/apache/commons/math/genetics/Chromosome.java
new file mode 100644
index 0000000..5641a5b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/Chromosome.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.math.genetics;
+
+/**
+ * Individual in a population. Chromosomes are compared based on their fitness.
+ *
+ * The chromosomes are IMMUTABLE, and so their fitness is also immutable and
+ * therefore it can be cached.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public abstract class Chromosome implements Comparable<Chromosome>,Fitness {
+
+ /**
+ * Cached value of the fitness of this chromosome.
+ */
+ private double fitness = Double.MIN_VALUE;
+
+ /**
+ * Access the fitness of this chromosome. The bigger the fitness, the better
+ * the chromosome.
+ *
+ * Computation of fitness is usually very time-consuming task, therefore the
+ * fitness is cached.
+ *
+ * @return the fitness.
+ */
+ public double getFitness() {
+ if (this.fitness == Double.MIN_VALUE) {
+ // 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>
+ * <li>1 if <code>another</code> is worse than <code>this</code></li>
+ * <li>0 if the two chromosomes have the same fitness</li>
+ * </ul>
+ */
+ public int compareTo(Chromosome another) {
+ return ((Double)this.getFitness()).compareTo(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(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(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(Population population) {
+ Chromosome sameChromosome = findSameChromosome(population);
+ if (sameChromosome != null) {
+ fitness = sameChromosome.getFitness();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/ChromosomePair.java b/src/main/java/org/apache/commons/math/genetics/ChromosomePair.java
new file mode 100644
index 0000000..82b048f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/ChromosomePair.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.math.genetics;
+
+/**
+ * A pair of {@link Chromosome} objects.
+ * @since 2.0
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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/math/genetics/CrossoverPolicy.java b/src/main/java/org/apache/commons/math/genetics/CrossoverPolicy.java
new file mode 100644
index 0000000..8742dac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/CrossoverPolicy.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.math.genetics;
+
+/**
+ * Policy used to create a pair of new chromosomes by performing a crossover
+ * operation on a source pair of chromosomes.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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.
+ */
+ ChromosomePair crossover(Chromosome first, Chromosome second);
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/ElitisticListPopulation.java b/src/main/java/org/apache/commons/math/genetics/ElitisticListPopulation.java
new file mode 100644
index 0000000..045632a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/ElitisticListPopulation.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.math.genetics;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Population of chromosomes which uses elitism (certain percentace of the best
+ * chromosomes is directly copied to the next generation).
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class ElitisticListPopulation extends ListPopulation {
+
+ /** percentage of chromosomes copied to the next generation */
+ private double elitismRate = 0.9;
+
+ /**
+ * Creates a new 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 %]
+ */
+ public ElitisticListPopulation(List<Chromosome> chromosomes, int populationLimit, double elitismRate) {
+ super(chromosomes, populationLimit);
+ this.elitismRate = elitismRate;
+ }
+
+ /**
+ * Creates a new ListPopulation 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 %]
+ */
+ public ElitisticListPopulation(int populationLimit, double elitismRate) {
+ super(populationLimit);
+ this.elitismRate = 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(this.getPopulationLimit(), this.getElitismRate());
+
+ List<Chromosome> oldChromosomes = this.getChromosomes();
+ Collections.sort(oldChromosomes);
+
+ // index of the last "not good enough" chromosome
+ int boundIndex = (int) FastMath.ceil((1.0 - this.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 %]
+ */
+ public void setElitismRate(double elitismRate) {
+ if (elitismRate < 0 || elitismRate > 1)
+ throw new IllegalArgumentException("Elitism rate has to be in [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/math/genetics/Fitness.java b/src/main/java/org/apache/commons/math/genetics/Fitness.java
new file mode 100644
index 0000000..40d674c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/Fitness.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.math.genetics;
+
+/**
+ * Fitness of a chromosome.
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @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/math/genetics/FixedGenerationCount.java b/src/main/java/org/apache/commons/math/genetics/FixedGenerationCount.java
new file mode 100644
index 0000000..337c5c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/FixedGenerationCount.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.math.genetics;
+
+/**
+ * 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.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @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
+ */
+ public FixedGenerationCount(int maxGenerations) {
+ if (maxGenerations <= 0)
+ throw new IllegalArgumentException("The number of generations has to be >= 0");
+ 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(Population population) {
+ if (this.numGenerations < this.maxGenerations) {
+ numGenerations++;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return the number of generations that have passed
+ */
+ public int getNumGenerations() {
+ return numGenerations;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/GeneticAlgorithm.java b/src/main/java/org/apache/commons/math/genetics/GeneticAlgorithm.java
new file mode 100644
index 0000000..fc666ac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/GeneticAlgorithm.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.math.genetics;
+
+import org.apache.commons.math.random.RandomGenerator;
+import org.apache.commons.math.random.JDKRandomGenerator;
+
+/**
+ * Implementation of a genetic algorithm. All factors that govern the operation
+ * of the algorithm can be configured for a specific problem.
+ *
+ * @since 2.0
+ * @version $Revision: 925812 $ $Date: 2010-03-21 16:49:31 +0100 (dim. 21 mars 2010) $
+ */
+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;
+
+ /**
+ * @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}
+ */
+ public GeneticAlgorithm(
+ CrossoverPolicy crossoverPolicy, double crossoverRate,
+ MutationPolicy mutationPolicy, double mutationRate,
+ SelectionPolicy selectionPolicy) {
+ if (crossoverRate < 0 || crossoverRate > 1) {
+ throw new IllegalArgumentException("crossoverRate must be between 0 and 1");
+ }
+ if (mutationRate < 0 || mutationRate > 1) {
+ throw new IllegalArgumentException("mutationRate must be between 0 and 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(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(Population initial, StoppingCondition condition) {
+ Population current = initial;
+ generationsEvolved = 0;
+ while (!condition.isSatisfied(current)) {
+ current = nextGeneration(current);
+ generationsEvolved++;
+ }
+ return current;
+ }
+
+ /**
+ * <p>Evolve the given population into the next generation.</p>
+ * <p><ol>
+ * <li>Get nextGeneration population to fill from <code>current</code>
+ * generation, using its nextGeneration method</li>
+ * <li>Loop until new generation is filled:</li>
+ * <ul><li>Apply configured SelectionPolicy to select a pair of parents
+ * from <code>current</code></li>
+ * <li>With probability = {@link #getCrossoverRate()}, apply
+ * configured {@link CrossoverPolicy} to parents</li>
+ * <li>With probability = {@link #getMutationRate()}, apply
+ * configured {@link MutationPolicy} to each of the offspring</li>
+ * <li>Add offspring individually to nextGeneration,
+ * space permitting</li>
+ * </ul>
+ * <li>Return nextGeneration</li>
+ * </ol>
+ * </p>
+ *
+ * @param current the current population.
+ * @return the population for the next generation.
+ */
+ public Population nextGeneration(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/math/genetics/InvalidRepresentationException.java b/src/main/java/org/apache/commons/math/genetics/InvalidRepresentationException.java
new file mode 100644
index 0000000..b60ded8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/InvalidRepresentationException.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.math.genetics;
+
+/**
+ * Exception indicating that the representation of a chromosome is not valid.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public class InvalidRepresentationException extends Exception {
+
+ /** Serialization version id */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor
+ */
+ public InvalidRepresentationException() {
+ super();
+ }
+
+ /**
+ * Construct an InvalidRepresentationException
+ * @param arg0 exception message
+ */
+ public InvalidRepresentationException(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * Construct an InvalidRepresentationException
+ * @param arg0 cause
+ */
+ public InvalidRepresentationException(Throwable arg0) {
+ super(arg0);
+ }
+
+ /**
+ * Construct an InvalidRepresentationException
+ *
+ * @param arg0 exception message
+ * @param arg1 cause
+ */
+ public InvalidRepresentationException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/ListPopulation.java b/src/main/java/org/apache/commons/math/genetics/ListPopulation.java
new file mode 100644
index 0000000..e880b2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/ListPopulation.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.math.genetics;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NotPositiveException;
+import org.apache.commons.math.exception.NumberIsTooLargeException;
+
+/**
+ * Population of chromosomes represented by a {@link List}.
+ *
+ * @since 2.0
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public abstract class ListPopulation implements Population {
+
+ /** List of chromosomes */
+ private List<Chromosome> chromosomes;
+
+ /** maximial size of the population */
+ private int populationLimit;
+
+
+ /**
+ * Creates a new ListPopulation instance.
+ *
+ * @param chromosomes list of chromosomes in the population
+ * @param populationLimit maximal size of the population
+ */
+ public ListPopulation (List<Chromosome> chromosomes, int populationLimit) {
+ if (chromosomes.size() > populationLimit) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+ chromosomes.size(), populationLimit, false);
+ }
+ if (populationLimit < 0) {
+ throw new NotPositiveException(LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit);
+ }
+
+ this.chromosomes = chromosomes;
+ this.populationLimit = populationLimit;
+ }
+
+ /**
+ * Creates a new ListPopulation instance and initializes its inner
+ * chromosome list.
+ *
+ * @param populationLimit maximal size of the population
+ */
+ public ListPopulation (int populationLimit) {
+ if (populationLimit < 0) {
+ throw new NotPositiveException(LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit);
+ }
+ this.populationLimit = populationLimit;
+ this.chromosomes = new ArrayList<Chromosome>(populationLimit);
+ }
+
+ /**
+ * Sets the list of chromosomes.
+ * @param chromosomes the list of chromosomes
+ */
+ public void setChromosomes(List<Chromosome> chromosomes) {
+ this.chromosomes = chromosomes;
+ }
+
+ /**
+ * Access the list of chromosomes.
+ * @return the list of chromosomes
+ */
+ public List<Chromosome> getChromosomes() {
+ return chromosomes;
+ }
+
+ /**
+ * Add the given chromosome to the population.
+ * @param chromosome the chromosome to add.
+ */
+ public void addChromosome(Chromosome chromosome) {
+ 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.
+ */
+ public void setPopulationLimit(int populationLimit) {
+ 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();
+ }
+
+ /**
+ * Chromosome list iterator
+ *
+ * @return chromosome iterator
+ */
+ public Iterator<Chromosome> iterator() {
+ return chromosomes.iterator();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/MutationPolicy.java b/src/main/java/org/apache/commons/math/genetics/MutationPolicy.java
new file mode 100644
index 0000000..9753db7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/MutationPolicy.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.math.genetics;
+
+/**
+ * Algorithm used to mutate a chrommosome.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public interface MutationPolicy {
+
+ /**
+ * Mutate the given chromosome.
+ * @param original the original chromosome.
+ * @return the mutated chromomsome.
+ */
+ Chromosome mutate(Chromosome original);
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/OnePointCrossover.java b/src/main/java/org/apache/commons/math/genetics/OnePointCrossover.java
new file mode 100644
index 0000000..f5f6ffd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/OnePointCrossover.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.math.genetics;
+
+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.
+ *
+ * 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 p2 = (0 1 1 0 1 0 | 0 1 1)
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it
+ * is parametrized by T. Moreover, the chromosomes must have same lengths.
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 2.0
+ * @version $Revision: 903046 $ $Date: 2010-01-26 03:07:26 +0100 (mar. 26 janv. 2010) $
+ *
+ */
+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.
+ *
+ * Example:
+ * -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 p2 = (0 1 1 0 1 0 | 0 1 1)
+ *
+ * @param first first parent (p1)
+ * @param second second parent (p2)
+ * @return pair of two children (c1,c2)
+ */
+ @SuppressWarnings("unchecked") // OK because of instanceof checks
+ public ChromosomePair crossover(Chromosome first, Chromosome second) {
+ if (! (first instanceof AbstractListChromosome<?> && second instanceof AbstractListChromosome<?>)) {
+ throw new IllegalArgumentException("One point crossover works on FixedLengthChromosomes only.");
+ }
+ 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.
+ */
+ private ChromosomePair crossover(AbstractListChromosome<T> first, AbstractListChromosome<T> second) {
+ int length = first.getLength();
+ if (length != second.getLength())
+ throw new IllegalArgumentException("Both chromosomes must have same lengths.");
+
+ // array representations of the parents
+ List<T> parent1Rep = first.getRepresentation();
+ List<T> parent2Rep = second.getRepresentation();
+ // and of the children
+ ArrayList<T> child1Rep = new ArrayList<T> (first.getLength());
+ ArrayList<T> child2Rep = new ArrayList<T> (second.getLength());
+
+ // select a crossover point at random (0 and length makes no sense)
+ 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/math/genetics/PermutationChromosome.java b/src/main/java/org/apache/commons/math/genetics/PermutationChromosome.java
new file mode 100644
index 0000000..676b5dc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/PermutationChromosome.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.math.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
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+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/math/genetics/Population.java b/src/main/java/org/apache/commons/math/genetics/Population.java
new file mode 100644
index 0000000..3fc758a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/Population.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.math.genetics;
+
+/**
+ * A collection of chromosomes that facilitates generational evolution.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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.
+ */
+ void addChromosome(Chromosome chromosome);
+
+ /**
+ * Access the fittest chromosome in this population.
+ * @return the fittest chromosome.
+ */
+ Chromosome getFittestChromosome();
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/RandomKey.java b/src/main/java/org/apache/commons/math/genetics/RandomKey.java
new file mode 100644
index 0000000..1cd28d3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/RandomKey.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.math.genetics;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * <p>
+ * 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>
+ *
+ * <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>
+ *
+ * <p>
+ * With this representation, common operators like n-point crossover can be
+ * used, because any such chromosome represents a valid permutation.
+ * </p>
+ *
+ * <p>
+ * Since the chromosome (and thus its arrayRepresentation) is immutable, the
+ * array representation is sorted only once in the constructor.
+ * </p>
+ *
+ * <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>
+ * <li>Rothlauf, F.: Representations for Genetic and Evolutionary Algorithms.
+ * Volume 104 of Studies in Fuzziness and Soft Computing. Physica-Verlag,
+ * Heidelberg (2002)</li>
+ * </ul>
+ * </p>
+ *
+ * @param <T>
+ * type of the permuted objects
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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 accorting to the representation (unmodifiable).
+ */
+ private final List<Integer> baseSeqPermutation;
+
+ /**
+ * Constructor.
+ *
+ * @param representation list of [0,1] values representing the permutation
+ */
+ public RandomKey(List<Double> representation) {
+ 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
+ */
+ public RandomKey(Double[] representation) {
+ this(Arrays.asList(representation));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<T> decode(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
+ */
+ private static <S> List<S> decodeGeneric(List<S> sequence, List<Double> representation, List<Double> sortedRepr) {
+ int l = sequence.size();
+
+ if (representation.size() != l) {
+ throw new IllegalArgumentException(String.format("Length of sequence for decoding (%s) has to be equal to the length of the RandomKey (%s)", l, representation.size()));
+ }
+ if (representation.size() != sortedRepr.size()) {
+ throw new IllegalArgumentException(String.format("Representation and sortedRepr must have same sizes, %d != %d", representation.size(), sortedRepr.size()));
+ }
+
+ List<Double> reprCopy = new ArrayList<Double> (representation);// do not modify the orig. 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(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(java.util.List<Double> chromosomeRepresentation) throws InvalidRepresentationException {
+ for (double val : chromosomeRepresentation) {
+ if (val < 0 || val > 1) {
+ throw new InvalidRepresentationException("Values of representation must be in [0,1] interval");
+ }
+ }
+ }
+
+
+ /**
+ * 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(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(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.
+ *
+ * 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(List<S> data, 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>.
+ *
+ * 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 IllegalArgumentException iff the <code>permutedData</code> and <code>originalData</code> contains different data
+ */
+ public static <S> List<Double> inducedPermutation(List<S> originalData, List<S> permutedData) throws IllegalArgumentException {
+ if (originalData.size() != permutedData.size()) {
+ throw new IllegalArgumentException("originalData and permutedData must have same length");
+ }
+ 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 IllegalArgumentException("originalData and permutedData must contain the same objects.");
+ }
+ 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(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/math/genetics/RandomKeyMutation.java b/src/main/java/org/apache/commons/math/genetics/RandomKeyMutation.java
new file mode 100644
index 0000000..792eef2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/RandomKeyMutation.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.math.genetics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class RandomKeyMutation implements MutationPolicy {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException if <code>original</code> is not a
+ * {@link RandomKey} instance
+ */
+ public Chromosome mutate(Chromosome original) {
+ if (!(original instanceof RandomKey<?>)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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/math/genetics/SelectionPolicy.java b/src/main/java/org/apache/commons/math/genetics/SelectionPolicy.java
new file mode 100644
index 0000000..4cf6768
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/SelectionPolicy.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.math.genetics;
+
+/**
+ * Algorithm used to select a chromosome pair from a population.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public interface SelectionPolicy {
+ /**
+ * Select two chromosomes from the population.
+ * @param population the population from which the chromosomes are choosen.
+ * @return the selected chromosomes.
+ */
+ ChromosomePair select(Population population);
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/StoppingCondition.java b/src/main/java/org/apache/commons/math/genetics/StoppingCondition.java
new file mode 100644
index 0000000..0253ce9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/StoppingCondition.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.math.genetics;
+
+/**
+ * Algorithm used to determine when to stop evolution.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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/math/genetics/TournamentSelection.java b/src/main/java/org/apache/commons/math/genetics/TournamentSelection.java
new file mode 100644
index 0000000..f1a091f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/TournamentSelection.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.math.genetics;
+
+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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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(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 choosen.
+ * @return the selected chromosomes.
+ */
+ public ChromosomePair select(Population population) {
+ 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 choosen.
+ * @return the selected chromosome.
+ */
+ private Chromosome tournament(ListPopulation population) {
+ if (population.getPopulationSize() < this.arity)
+ throw new IllegalArgumentException("Tournament arity cannot be bigger than population size.");
+ // auxiliary population
+ ListPopulation tournamentPopulation = new ListPopulation(this.arity) {
+ 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(int arity) {
+ this.arity = arity;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/genetics/package.html b/src/main/java/org/apache/commons/math/genetics/package.html
new file mode 100644
index 0000000..adcd5a9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/genetics/package.html
@@ -0,0 +1,24 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 784604 $ -->
+<body>
+<p>
+This package provides Genetic Algorithms components and implementations.
+</p>
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java b/src/main/java/org/apache/commons/math/geometry/CardanEulerSingularityException.java
new file mode 100644
index 0000000..61d349e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/CardanEulerSingularityException.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.math.geometry;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/** This class represents exceptions thrown while extractiong Cardan
+ * or Euler angles from a rotation.
+
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 1.2
+ */
+public class CardanEulerSingularityException
+ extends MathException {
+
+ /** 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/math/geometry/NotARotationMatrixException.java b/src/main/java/org/apache/commons/math/geometry/NotARotationMatrixException.java
new file mode 100644
index 0000000..0b6ff8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/NotARotationMatrixException.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.math.geometry;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This class represents exceptions thrown while building rotations
+ * from matrices.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 1.2
+ */
+
+public class NotARotationMatrixException
+ extends MathException {
+
+ /** 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)
+ * @deprecated as of 2.2 replaced by {@link #NotARotationMatrixException(Localizable, Object...)}
+ */
+ @Deprecated
+ public NotARotationMatrixException(String specifier, Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /**
+ * 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/math/geometry/Rotation.java b/src/main/java/org/apache/commons/math/geometry/Rotation.java
new file mode 100644
index 0000000..ee3f4b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/Rotation.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.math.geometry;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1067500 $ $Date: 2011-02-05 21:11:30 +0100 (sam. 05 févr. 2011) $
+ * @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>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(Vector3D) 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 ArithmeticException if the axis norm is zero
+ */
+ public Rotation(Vector3D axis, double angle) {
+
+ double norm = axis.getNorm();
+ if (norm == 0) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_AXIS);
+ }
+
+ double halfAngle = -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);
+ }
+
+ // 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
+ q0 = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / q0;
+ q1 = inv * (ort[1][2] - ort[2][1]);
+ q2 = inv * (ort[2][0] - ort[0][2]);
+ q3 = 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
+ q1 = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / q1;
+ q0 = inv * (ort[1][2] - ort[2][1]);
+ q2 = inv * (ort[0][1] + ort[1][0]);
+ q3 = 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
+ q2 = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / q2;
+ q0 = inv * (ort[2][0] - ort[0][2]);
+ q1 = inv * (ort[0][1] + ort[1][0]);
+ q3 = 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];
+ q3 = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / q3;
+ q0 = inv * (ort[0][1] - ort[1][0]);
+ q1 = inv * (ort[0][2] + ort[2][0]);
+ q2 = inv * (ort[2][1] + ort[1][2]);
+ }
+ }
+ }
+
+ }
+
+ /** Build the rotation that transforms a pair of vector 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 (v<sub>1</sub>,
+ * v<sub>2</sub>) 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 IllegalArgumentException if the norm of one of the vectors is zero
+ */
+ public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2) {
+
+ // norms computation
+ double u1u1 = Vector3D.dotProduct(u1, u1);
+ double u2u2 = Vector3D.dotProduct(u2, u2);
+ double v1v1 = Vector3D.dotProduct(v1, v1);
+ double v2v2 = Vector3D.dotProduct(v2, v2);
+ if ((u1u1 == 0) || (u2u2 == 0) || (v1v1 == 0) || (v2v2 == 0)) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
+ }
+
+ double u1x = u1.getX();
+ double u1y = u1.getY();
+ double u1z = u1.getZ();
+
+ double u2x = u2.getX();
+ double u2y = u2.getY();
+ double u2z = u2.getZ();
+
+ // normalize v1 in order to have (v1'|v1') = (u1|u1)
+ double coeff = FastMath.sqrt (u1u1 / v1v1);
+ double v1x = coeff * v1.getX();
+ double v1y = coeff * v1.getY();
+ double v1z = coeff * v1.getZ();
+ v1 = new Vector3D(v1x, v1y, v1z);
+
+ // adjust v2 in order to have (u1|u2) = (v1|v2) and (v2'|v2') = (u2|u2)
+ double u1u2 = Vector3D.dotProduct(u1, u2);
+ double v1v2 = Vector3D.dotProduct(v1, v2);
+ double coeffU = u1u2 / u1u1;
+ double coeffV = v1v2 / u1u1;
+ double beta = FastMath.sqrt((u2u2 - u1u2 * coeffU) / (v2v2 - v1v2 * coeffV));
+ double alpha = coeffU - beta * coeffV;
+ double v2x = alpha * v1x + beta * v2.getX();
+ double v2y = alpha * v1y + beta * v2.getY();
+ double v2z = alpha * v1z + beta * v2.getZ();
+ v2 = new Vector3D(v2x, v2y, v2z);
+
+ // preliminary computation (we use explicit formulation instead
+ // of relying on the Vector3D class in order to avoid building lots
+ // of temporary objects)
+ Vector3D uRef = u1;
+ Vector3D vRef = v1;
+ double dx1 = v1x - u1.getX();
+ double dy1 = v1y - u1.getY();
+ double dz1 = v1z - u1.getZ();
+ double dx2 = v2x - u2.getX();
+ double dy2 = v2y - u2.getY();
+ double dz2 = v2z - u2.getZ();
+ Vector3D k = new Vector3D(dy1 * dz2 - dz1 * dy2,
+ dz1 * dx2 - dx1 * dz2,
+ dx1 * dy2 - dy1 * dx2);
+ double c = k.getX() * (u1y * u2z - u1z * u2y) +
+ k.getY() * (u1z * u2x - u1x * u2z) +
+ k.getZ() * (u1x * u2y - u1y * u2x);
+
+ if (c == 0) {
+ // the (q1, q2, q3) vector is in the (u1, u2) plane
+ // we try other vectors
+ Vector3D u3 = Vector3D.crossProduct(u1, u2);
+ Vector3D v3 = Vector3D.crossProduct(v1, v2);
+ double u3x = u3.getX();
+ double u3y = u3.getY();
+ double u3z = u3.getZ();
+ double v3x = v3.getX();
+ double v3y = v3.getY();
+ double v3z = v3.getZ();
+
+ double dx3 = v3x - u3x;
+ double dy3 = v3y - u3y;
+ double dz3 = v3z - u3z;
+ k = new Vector3D(dy1 * dz3 - dz1 * dy3,
+ dz1 * dx3 - dx1 * dz3,
+ dx1 * dy3 - dy1 * dx3);
+ c = k.getX() * (u1y * u3z - u1z * u3y) +
+ k.getY() * (u1z * u3x - u1x * u3z) +
+ k.getZ() * (u1x * u3y - u1y * u3x);
+
+ if (c == 0) {
+ // the (q1, q2, q3) vector is aligned with u1:
+ // we try (u2, u3) and (v2, v3)
+ k = new Vector3D(dy2 * dz3 - dz2 * dy3,
+ dz2 * dx3 - dx2 * dz3,
+ dx2 * dy3 - dy2 * dx3);
+ c = k.getX() * (u2y * u3z - u2z * u3y) +
+ k.getY() * (u2z * u3x - u2x * u3z) +
+ k.getZ() * (u2x * u3y - u2y * u3x);
+
+ if (c == 0) {
+ // the (q1, q2, q3) vector is aligned with everything
+ // this is really the identity rotation
+ q0 = 1.0;
+ q1 = 0.0;
+ q2 = 0.0;
+ q3 = 0.0;
+ return;
+ }
+
+ // we will have to use u2 and v2 to compute the scalar part
+ uRef = u2;
+ vRef = v2;
+
+ }
+
+ }
+
+ // compute the vectorial part
+ c = FastMath.sqrt(c);
+ double inv = 1.0 / (c + c);
+ q1 = inv * k.getX();
+ q2 = inv * k.getY();
+ q3 = inv * k.getZ();
+
+ // compute the scalar part
+ k = new Vector3D(uRef.getY() * q3 - uRef.getZ() * q2,
+ uRef.getZ() * q1 - uRef.getX() * q3,
+ uRef.getX() * q2 - uRef.getY() * q1);
+ c = Vector3D.dotProduct(k, k);
+ q0 = Vector3D.dotProduct(vRef, k) / (c + c);
+
+ }
+
+ /** 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 colinear, an
+ * arbitrary rotation axis is chosen.</p>
+ *
+ * @param u origin vector
+ * @param v desired image of u by the rotation
+ * @exception IllegalArgumentException if the norm of one of the vectors is zero
+ */
+ public Rotation(Vector3D u, Vector3D v) {
+
+ double normProduct = u.getNorm() * v.getNorm();
+ if (normProduct == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
+ }
+
+ double dot = Vector3D.dotProduct(u, 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);
+ q1 = coeff * (v.getY() * u.getZ() - v.getZ() * u.getY());
+ q2 = coeff * (v.getZ() * u.getX() - v.getX() * u.getZ());
+ q3 = coeff * (v.getX() * u.getY() - v.getY() * u.getX());
+ }
+
+ }
+
+ /** 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
+ */
+ public Rotation(RotationOrder order,
+ double alpha1, double alpha2, double alpha3) {
+ Rotation r1 = new Rotation(order.getA1(), alpha1);
+ Rotation r2 = new Rotation(order.getA2(), alpha2);
+ Rotation r3 = new Rotation(order.getA3(), alpha3);
+ Rotation composed = r1.applyTo(r2.applyTo(r3));
+ q0 = composed.q0;
+ q1 = composed.q1;
+ q2 = composed.q2;
+ q3 = composed.q3;
+ }
+
+ /** 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.
+ * @return normalized axis of the rotation
+ * @see #Rotation(Vector3D, double)
+ */
+ public Vector3D getAxis() {
+ double squaredSine = q1 * q1 + q2 * q2 + q3 * q3;
+ if (squaredSine == 0) {
+ return new Vector3D(1, 0, 0);
+ } else if (q0 < 0) {
+ double inverse = 1 / FastMath.sqrt(squaredSine);
+ return new Vector3D(q1 * inverse, q2 * inverse, q3 * inverse);
+ }
+ double inverse = -1 / 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>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
+ */
+ public double[] getAngles(RotationOrder order)
+ throws CardanEulerSingularityException {
+
+ 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())
+ };
+
+ }
+
+ }
+
+ /** 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 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 instance to another rotation.
+ * Applying the instance to a rotation is computing the composition
+ * in an order compliant with the following rule : let u be any
+ * vector and v its image by r (i.e. r.applyTo(u) = v), let w be the image
+ * of v by the instance (i.e. applyTo(v) = w), then w = comp.applyTo(u),
+ * where comp = applyTo(r).
+ * @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 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.
+ * Applying the inverse of the instance to a rotation is computing
+ * the composition in an order compliant with the following rule :
+ * let u be any vector and v its image by r (i.e. r.applyTo(u) = v),
+ * let w be the inverse image of v by the instance
+ * (i.e. applyInverseTo(v) = w), then w = comp.applyTo(u), where
+ * comp = applyInverseTo(r).
+ * @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 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.applyInverseTo(r2).getAngle();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/geometry/RotationOrder.java b/src/main/java/org/apache/commons/math/geometry/RotationOrder.java
new file mode 100644
index 0000000..9292b14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/RotationOrder.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.math.geometry;
+
+/**
+ * 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.
+ *
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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/math/geometry/Vector3D.java b/src/main/java/org/apache/commons/math/geometry/Vector3D.java
new file mode 100644
index 0000000..db18795
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/Vector3D.java
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.geometry;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class implements vectors in a three-dimensional space.
+ * <p>Instance of this class are guaranteed to be immutable.</p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 1.2
+ */
+
+public class Vector3D
+ implements Serializable {
+
+ /** 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);
+
+ /** Default format. */
+ private static final Vector3DFormat DEFAULT_FORMAT =
+ Vector3DFormat.getInstance();
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 5133268763396045979L;
+
+ /** 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 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 = a1 * u1.x + a2 * u2.x;
+ this.y = a1 * u1.y + a2 * u2.y;
+ this.z = 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 = a1 * u1.x + a2 * u2.x + a3 * u3.x;
+ this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
+ this.z = 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 = 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;
+ this.z = 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 L<sub>1</sub> norm for the vector.
+ * @return L<sub>1</sub> norm for the vector
+ */
+ public double getNorm1() {
+ return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
+ }
+
+ /** Get the L<sub>2</sub> norm for the vector.
+ * @return euclidian norm for the vector
+ */
+ public double getNorm() {
+ return FastMath.sqrt (x * x + y * y + z * z);
+ }
+
+ /** Get the square of the norm for the vector.
+ * @return square of the euclidian norm for the vector
+ */
+ public double getNormSq() {
+ return x * x + y * y + z * z;
+ }
+
+ /** Get the L<sub>&infin;</sub> norm for the vector.
+ * @return L<sub>&infin;</sub> norm for the vector
+ */
+ 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());
+ }
+
+ /** Add a vector to the instance.
+ * @param v vector to add
+ * @return a new vector
+ */
+ public Vector3D add(Vector3D v) {
+ return new Vector3D(x + v.x, y + v.y, z + v.z);
+ }
+
+ /** 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 Vector3D add(double factor, Vector3D v) {
+ return new Vector3D(x + factor * v.x, y + factor * v.y, z + factor * v.z);
+ }
+
+ /** Subtract a vector from the instance.
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public Vector3D subtract(Vector3D v) {
+ return new Vector3D(x - v.x, y - v.y, z - v.z);
+ }
+
+ /** 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 Vector3D subtract(double factor, Vector3D v) {
+ return new Vector3D(x - factor * v.x, y - factor * v.y, z - factor * v.z);
+ }
+
+ /** Get a normalized vector aligned with the instance.
+ * @return a new normalized vector
+ * @exception ArithmeticException if the norm is zero
+ */
+ public Vector3D normalize() {
+ double s = getNorm();
+ if (s == 0) {
+ throw MathRuntimeException.createArithmeticException(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 ArithmeticException if the norm of the instance is null
+ */
+ public Vector3D orthogonal() {
+
+ double threshold = 0.6 * getNorm();
+ if (threshold == 0) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ if ((x >= -threshold) && (x <= threshold)) {
+ double inverse = 1 / FastMath.sqrt(y * y + z * z);
+ return new Vector3D(0, inverse * z, -inverse * y);
+ } else if ((y >= -threshold) && (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 ArithmeticException if either vector has a null norm
+ */
+ public static double angle(Vector3D v1, Vector3D v2) {
+
+ double normProduct = v1.getNorm() * v2.getNorm();
+ if (normProduct == 0) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ double dot = dotProduct(v1, 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);
+
+ }
+
+ /** Get the opposite of the instance.
+ * @return a new vector which is opposite to the instance
+ */
+ public Vector3D negate() {
+ return new Vector3D(-x, -y, -z);
+ }
+
+ /** Multiply the instance by a scalar
+ * @param a scalar
+ * @return a new vector
+ */
+ public Vector3D scalarMultiply(double a) {
+ return new Vector3D(a * x, a * y, a * z);
+ }
+
+ /**
+ * 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) || Double.isNaN(y) || Double.isNaN(z);
+ }
+
+ /**
+ * 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) || 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 8;
+ }
+ return 31 * (23 * MathUtils.hash(x) + 19 * MathUtils.hash(y) + MathUtils.hash(z));
+ }
+
+ /** 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.x * v2.x + v1.y * v2.y + v1.z * v2.z;
+ }
+
+ /** 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(Vector3D v1, Vector3D v2) {
+ return new Vector3D(v1.y * v2.z - v1.z * v2.y,
+ v1.z * v2.x - v1.x * v2.z,
+ v1.x * v2.y - v1.y * 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
+ * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
+ */
+ public static double distance1(Vector3D v1, Vector3D v2) {
+ final double dx = FastMath.abs(v2.x - v1.x);
+ final double dy = FastMath.abs(v2.y - v1.y);
+ final double dz = FastMath.abs(v2.z - v1.z);
+ return dx + dy + dz;
+ }
+
+ /** 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) {
+ final double dx = v2.x - v1.x;
+ final double dy = v2.y - v1.y;
+ final double dz = v2.z - v1.z;
+ return FastMath.sqrt(dx * dx + dy * dy + dz * dz);
+ }
+
+ /** 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) {
+ final double dx = FastMath.abs(v2.x - v1.x);
+ final double dy = FastMath.abs(v2.y - v1.y);
+ final double dz = FastMath.abs(v2.z - v1.z);
+ return FastMath.max(FastMath.max(dx, dy), dz);
+ }
+
+ /** 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) {
+ final double dx = v2.x - v1.x;
+ final double dy = v2.y - v1.y;
+ final double dz = v2.z - v1.z;
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+ /** Get a string representation of this vector.
+ * @return a string representation of this vector
+ */
+ @Override
+ public String toString() {
+ return DEFAULT_FORMAT.format(this);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/geometry/Vector3DFormat.java b/src/main/java/org/apache/commons/math/geometry/Vector3DFormat.java
new file mode 100644
index 0000000..7400e20
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/Vector3DFormat.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.geometry;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ */
+public class Vector3DFormat extends CompositeFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -5447606608652576301L;
+
+ /** 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.</p>
+ */
+ public Vector3DFormat() {
+ this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ * @param format the custom format for components.
+ */
+ public Vector3DFormat(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 Vector3DFormat(final String prefix, final String suffix,
+ final String separator) {
+ this(prefix, suffix, separator, 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) {
+ 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 3D vectors formats are available.
+ * <p>This is the same set as the {@link NumberFormat} set.</p>
+ * @return available 3D 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 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(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * This static method calls {@link #format(Object)} on a default instance of
+ * Vector3DFormat.
+ *
+ * @param v Vector3D object to format
+ * @return A formatted vector
+ */
+ public static String formatVector3D(Vector3D v) {
+ return getInstance().format(v);
+ }
+
+ /**
+ * 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.
+ */
+ public StringBuffer format(Vector3D vector, StringBuffer toAppendTo,
+ FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ // format prefix
+ toAppendTo.append(prefix);
+
+ // format components
+ formatDouble(vector.getX(), format, toAppendTo, pos);
+ toAppendTo.append(separator);
+ formatDouble(vector.getY(), format, toAppendTo, pos);
+ toAppendTo.append(separator);
+ formatDouble(vector.getZ(), format, toAppendTo, pos);
+
+ // format suffix
+ toAppendTo.append(suffix);
+
+ return toAppendTo;
+
+ }
+
+ /**
+ * Formats a object to produce a string.
+ * <p><code>obj</code> must be a {@link Vector3D} object. Any other type of
+ * object will result in an {@link IllegalArgumentException} being thrown.</p>
+ * @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 IllegalArgumentException is <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo,
+ FieldPosition pos) {
+
+ if (obj instanceof Vector3D) {
+ return format( (Vector3D)obj, toAppendTo, pos);
+ }
+
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR,
+ obj.getClass().getName());
+
+ }
+
+ /**
+ * Parses a string to produce a {@link Vector3D} object.
+ * @param source the string to parse
+ * @return the parsed {@link Vector3D} object.
+ * @exception ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ public Vector3D parse(String source) throws ParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Vector3D result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw MathRuntimeException.createParseException(
+ parsePosition.getErrorIndex(),
+ LocalizedFormats.UNPARSEABLE_3D_VECTOR, source);
+ }
+ 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.
+ */
+ public Vector3D parse(String source, ParsePosition pos) {
+ int initialIndex = pos.getIndex();
+
+ // parse prefix
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedPrefix, pos)) {
+ return null;
+ }
+
+ // parse X component
+ parseAndIgnoreWhitespace(source, pos);
+ Number x = parseNumber(source, format, pos);
+ if (x == null) {
+ // invalid abscissa
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse Y component
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedSeparator, pos)) {
+ return null;
+ }
+ parseAndIgnoreWhitespace(source, pos);
+ Number y = parseNumber(source, format, pos);
+ if (y == null) {
+ // invalid ordinate
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse Z component
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedSeparator, pos)) {
+ return null;
+ }
+ parseAndIgnoreWhitespace(source, pos);
+ Number z = parseNumber(source, format, pos);
+ if (z == null) {
+ // invalid height
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse suffix
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedSuffix, pos)) {
+ return null;
+ }
+
+ return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue());
+
+ }
+
+ /**
+ * Parses a string to produce a object.
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed object.
+ * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
+ */
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return parse(source, pos);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/geometry/package.html b/src/main/java/org/apache/commons/math/geometry/package.html
new file mode 100644
index 0000000..d528d4a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/geometry/package.html
@@ -0,0 +1,24 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 613610 $ -->
+<body>
+<p>
+This package provides basic 3D geometry components.
+</p>
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/linear/AbstractFieldMatrix.java b/src/main/java/org/apache/commons/math/linear/AbstractFieldMatrix.java
new file mode 100644
index 0000000..d35f25f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/AbstractFieldMatrix.java
@@ -0,0 +1,1139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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. </p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @throws IllegalArgumentException if row or column dimension is not positive
+ */
+ protected AbstractFieldMatrix(final Field<T> field,
+ final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ if (rowDimension < 1 ) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, rowDimension, 1);
+ }
+ if (columnDimension < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, columnDimension, 1);
+ }
+ this.field = field;
+ }
+
+ /**
+ * Get the elements type from an array.
+ * @param <T> the type of the field elements
+ * @param d data array
+ * @return field to which array elements belong
+ * @exception IllegalArgumentException if array is empty
+ */
+ protected static <T extends FieldElement<T>> Field<T> extractField(final T[][] d)
+ throws IllegalArgumentException {
+ if (d.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ if (d[0].length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ return d[0][0].getField();
+ }
+
+ /**
+ * Get the elements type from an array.
+ * @param <T> the type of the field elements
+ * @param d data array
+ * @return field to which array elements belong
+ * @exception IllegalArgumentException if array is empty
+ */
+ protected static <T extends FieldElement<T>> Field<T> extractField(final T[] d)
+ throws IllegalArgumentException {
+ if (d.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ return d[0].getField();
+ }
+
+ /** Build an array of elements.
+ * <p>
+ * Complete arrays are filled with field.getZero()
+ * </p>
+ * @param <T> the 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
+ */
+ @SuppressWarnings("unchecked")
+ protected static <T extends FieldElement<T>> T[][] buildArray(final Field<T> field,
+ final int rows,
+ final int columns) {
+ if (columns < 0) {
+ T[] dummyRow = (T[]) Array.newInstance(field.getZero().getClass(), 0);
+ return (T[][]) Array.newInstance(dummyRow.getClass(), rows);
+ }
+ T[][] array =
+ (T[][]) Array.newInstance(field.getZero().getClass(), new int[] { rows, columns });
+ for (int i = 0; i < array.length; ++i) {
+ Arrays.fill(array[i], field.getZero());
+ }
+ return array;
+ }
+
+ /** Build an array of elements.
+ * <p>
+ * Arrays are filled with field.getZero()
+ * </p>
+ * @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
+ */
+ protected static <T extends FieldElement<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.getZero().getClass(), length);
+ Arrays.fill(array, field.getZero());
+ return array;
+ }
+
+ /** {@inheritDoc} */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public abstract FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException;
+
+ /** {@inheritDoc} */
+ public abstract FieldMatrix<T> copy();
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> add(FieldMatrix<T> m) throws IllegalArgumentException {
+
+ // 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 IllegalArgumentException {
+
+ // 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 IllegalArgumentException {
+
+ // 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 IllegalArgumentException {
+ return m.multiply(this);
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getData() {
+
+ final T[][] data = 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 MatrixIndexException {
+
+ 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 MatrixIndexException {
+
+ // 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 MatrixIndexException, IllegalArgumentException {
+
+ // 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException, IllegalArgumentException {
+
+ // safety checks
+ checkSubMatrixIndex(selectedRows, selectedColumns);
+ if ((destination.length < selectedRows.length) ||
+ (destination[0].length < selectedColumns.length)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+
+ for (int r = 1; r < nRows; ++r) {
+ if (subMatrix[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) ||
+ (matrix.getColumnDimension() != nCols)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) ||
+ (matrix.getColumnDimension() != 1)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ return new ArrayFieldVector<T>(getRow(row), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setRowVector(final int row, final FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (vector.getDimension() != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ return new ArrayFieldVector<T>(getColumn(column), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnVector(final int column, final FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (vector.getDimension() != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ final T[] out = 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 MatrixIndexException, InvalidMatrixException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ final T[] out = 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 MatrixIndexException, InvalidMatrixException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void setEntry(int row, int column, T value)
+ throws MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void addToEntry(int row, int column, T increment)
+ throws MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void multiplyEntry(int row, int column, T factor)
+ throws MatrixIndexException;
+
+ /** {@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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, nCols);
+ }
+
+ final T[] out = 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 IllegalArgumentException {
+ try {
+ return new ArrayFieldVector<T>(operate(((ArrayFieldVector<T>) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.getDimension(), nCols);
+ }
+
+ final T[] out = 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>(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T[] preMultiply(final T[] v)
+ throws IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, nRows);
+ }
+
+ final T[] out = 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 IllegalArgumentException {
+ try {
+ return new ArrayFieldVector<T>(preMultiply(((ArrayFieldVector<T>) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.getDimension(), nRows);
+ }
+
+ final T[] out = 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>(out);
+
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor)
+ throws MatrixVisitorException {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow, final int endRow,
+ final int startColumn, final int endColumn)
+ throws MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 StringBuilder res = new StringBuilder();
+ 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
+ * @exception MatrixIndexException if index is not valid
+ */
+ protected void checkRowIndex(final int row) {
+ if (row < 0 || row >= getRowDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.ROW_INDEX_OUT_OF_RANGE,
+ row, 0, getRowDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if a column index is valid.
+ * @param column column index to check
+ * @exception MatrixIndexException if index is not valid
+ */
+ protected void checkColumnIndex(final int column)
+ throws MatrixIndexException {
+ if (column < 0 || column >= getColumnDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.COLUMN_INDEX_OUT_OF_RANGE,
+ 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
+ * @exception MatrixIndexException if the indices are not valid
+ */
+ protected void checkSubMatrixIndex(final int startRow, final int endRow,
+ final int startColumn, final int endColumn) {
+ checkRowIndex(startRow);
+ checkRowIndex(endRow);
+ if (startRow > endRow) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
+ startRow, endRow);
+ }
+
+ checkColumnIndex(startColumn);
+ checkColumnIndex(endColumn);
+ if (startColumn > endColumn) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
+ startColumn, endColumn);
+ }
+
+
+ }
+
+ /**
+ * 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.
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ protected void checkSubMatrixIndex(final int[] selectedRows, final int[] selectedColumns) {
+ if (selectedRows.length * selectedColumns.length == 0) {
+ if (selectedRows.length == 0) {
+ throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
+ }
+ throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY);
+ }
+
+ 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
+ * @exception IllegalArgumentException if matrix is not addition compatible with instance
+ */
+ protected void checkAdditionCompatible(final FieldMatrix<T> m) {
+ if ((getRowDimension() != m.getRowDimension()) ||
+ (getColumnDimension() != m.getColumnDimension())) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_ADDITION_COMPATIBLE_MATRICES,
+ getRowDimension(), getColumnDimension(),
+ m.getRowDimension(), m.getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if a matrix is subtraction compatible with the instance
+ * @param m matrix to check
+ * @exception IllegalArgumentException if matrix is not subtraction compatible with instance
+ */
+ protected void checkSubtractionCompatible(final FieldMatrix<T> m) {
+ if ((getRowDimension() != m.getRowDimension()) ||
+ (getColumnDimension() != m.getColumnDimension())) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_SUBTRACTION_COMPATIBLE_MATRICES,
+ getRowDimension(), getColumnDimension(),
+ m.getRowDimension(), m.getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if a matrix is multiplication compatible with the instance
+ * @param m matrix to check
+ * @exception IllegalArgumentException if matrix is not multiplication compatible with instance
+ */
+ protected void checkMultiplicationCompatible(final FieldMatrix<T> m) {
+ if (getColumnDimension() != m.getRowDimension()) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_MULTIPLICATION_COMPATIBLE_MATRICES,
+ getRowDimension(), getColumnDimension(),
+ m.getRowDimension(), m.getColumnDimension());
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java b/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
new file mode 100644
index 0000000..4bf7a82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/AbstractRealMatrix.java
@@ -0,0 +1,1071 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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. </p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public abstract class AbstractRealMatrix implements RealMatrix {
+
+
+ /** Cached LU solver.
+ * @deprecated as of release 2.0, since all methods using this are deprecated
+ */
+ @Deprecated
+ private DecompositionSolver lu;
+
+ /**
+ * Creates a matrix with no data
+ */
+ protected AbstractRealMatrix() {
+ lu = null;
+ }
+
+ /**
+ * 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 IllegalArgumentException if row or column dimension is not positive
+ */
+ protected AbstractRealMatrix(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ if (rowDimension < 1 ) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, rowDimension, 1);
+ }
+ if (columnDimension <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, columnDimension, 1);
+ }
+ lu = null;
+ }
+
+ /** {@inheritDoc} */
+ public abstract RealMatrix createMatrix(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException;
+
+ /** {@inheritDoc} */
+ public abstract RealMatrix copy();
+
+ /** {@inheritDoc} */
+ public RealMatrix add(RealMatrix m) throws IllegalArgumentException {
+
+ // safety check
+ 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 IllegalArgumentException {
+
+ // safety check
+ 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 IllegalArgumentException {
+
+ // safety check
+ 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 IllegalArgumentException {
+ return m.multiply(this);
+ }
+
+ /** {@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 MatrixIndexException {
+
+ 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 MatrixIndexException {
+
+ // safety checks
+ MatrixUtils.checkSubMatrixIndex(this, selectedRows, selectedColumns);
+
+ // copy entries
+ 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 MatrixIndexException, IllegalArgumentException {
+
+ // safety checks
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ destination.length, destination[0].length,
+ rowsCount, columnsCount);
+ }
+
+ // copy entries
+ 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 MatrixIndexException, IllegalArgumentException {
+
+ // safety checks
+ MatrixUtils.checkSubMatrixIndex(this, selectedRows, selectedColumns);
+ if ((destination.length < selectedRows.length) ||
+ (destination[0].length < selectedColumns.length)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ destination.length, destination[0].length,
+ selectedRows.length, selectedColumns.length);
+ }
+
+ // copy entries
+ for (int i = 0; i < selectedRows.length; i++) {
+ final double[] destinationI = destination[i];
+ 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 MatrixIndexException {
+
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+
+ for (int r = 1; r < nRows; ++r) {
+ if (subMatrix[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ 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]);
+ }
+ }
+
+ lu = null;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getRowMatrix(final int row)
+ throws MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) ||
+ (matrix.getColumnDimension() != nCols)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) ||
+ (matrix.getColumnDimension() != 1)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ return new ArrayRealVector(getRow(row), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setRowVector(final int row, final RealVector vector)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (vector.getDimension() != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ return new ArrayRealVector(getColumn(column), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnVector(final int column, final RealVector vector)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (vector.getDimension() != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ array.length, 1, nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, array[i]);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public abstract double getEntry(int row, int column)
+ throws MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void setEntry(int row, int column, double value)
+ throws MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void addToEntry(int row, int column, double increment)
+ throws MatrixIndexException;
+
+ /** {@inheritDoc} */
+ public abstract void multiplyEntry(int row, int column, double factor)
+ throws MatrixIndexException;
+
+ /** {@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} */
+ @Deprecated
+ public RealMatrix inverse()
+ throws InvalidMatrixException {
+ if (lu == null) {
+ lu = new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getSolver();
+ }
+ return lu.getInverse();
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double getDeterminant()
+ throws InvalidMatrixException {
+ return new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getDeterminant();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSquare() {
+ return getColumnDimension() == getRowDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public boolean isSingular() {
+ if (lu == null) {
+ lu = new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getSolver();
+ }
+ return !lu.isNonSingular();
+ }
+
+ /** {@inheritDoc} */
+ public abstract int getRowDimension();
+
+ /** {@inheritDoc} */
+ 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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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} */
+ public RealVector operate(final RealVector v)
+ throws IllegalArgumentException {
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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 IllegalArgumentException {
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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);
+
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(final RealMatrixChangingVisitor visitor)
+ throws MatrixVisitorException {
+ 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);
+ }
+ }
+ lu = null;
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(final RealMatrixPreservingVisitor visitor)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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);
+ }
+ }
+ lu = null;
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(final RealMatrixPreservingVisitor visitor,
+ final int startRow, final int endRow,
+ final int startColumn, final int endColumn)
+ throws MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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);
+ }
+ }
+ lu = null;
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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);
+ }
+ }
+ lu = null;
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor,
+ final int startRow, final int endRow,
+ final int startColumn, final int endColumn)
+ throws MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor)
+ throws MatrixVisitorException {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor,
+ final int startRow, final int endRow,
+ final int startColumn, final int endColumn)
+ throws MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public double[] solve(final double[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ if (lu == null) {
+ lu = new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getSolver();
+ }
+ return lu.solve(b);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public RealMatrix solve(final RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ if (lu == null) {
+ lu = new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getSolver();
+ }
+ return lu.solve(b);
+ }
+
+ /**
+ * Computes a new
+ * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
+ * LU decomposition</a> for this matrix, storing the result for use by other methods.
+ * <p>
+ * <strong>Implementation Note</strong>:<br>
+ * Uses <a href="http://www.damtp.cam.ac.uk/user/fdl/people/sd/lectures/nummeth98/linear.htm">
+ * Crout's algorithm</a>, with partial pivoting.</p>
+ * <p>
+ * <strong>Usage Note</strong>:<br>
+ * This method should rarely be invoked directly. Its only use is
+ * to force recomputation of the LU decomposition when changes have been
+ * made to the underlying data using direct array references. Changes
+ * made using setXxx methods will trigger recomputation when needed
+ * automatically.</p>
+ *
+ * @throws InvalidMatrixException if the matrix is non-square or singular.
+ * @deprecated as of release 2.0, replaced by {@link LUDecomposition}
+ */
+ @Deprecated
+ public void luDecompose()
+ throws InvalidMatrixException {
+ if (lu == null) {
+ lu = new LUDecompositionImpl(this, MathUtils.SAFE_MIN).getSolver();
+ }
+ }
+
+ /**
+ * 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 StringBuilder res = new StringBuilder();
+ 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>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;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/AbstractRealVector.java b/src/main/java/org/apache/commons/math/linear/AbstractRealVector.java
new file mode 100644
index 0000000..e39c9ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/AbstractRealVector.java
@@ -0,0 +1,932 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.MathUnsupportedOperationException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.analysis.BinaryFunction;
+import org.apache.commons.math.analysis.ComposableFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class provides default basic implementations for many methods in the
+ * {@link RealVector} interface.
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.1
+ */
+public abstract class AbstractRealVector implements RealVector {
+
+ /**
+ * 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 dimension
+ */
+ protected void checkVectorDimensions(RealVector v) {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n expected dimension.
+ * @exception DimensionMismatchException if the dimension is
+ * inconsistent with 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 MatrixIndexException if index is not valid
+ */
+ protected void checkIndex(final int index)
+ throws MatrixIndexException {
+ if (index < 0 || index >= getDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.INDEX_OUT_OF_RANGE,
+ index, 0, getDimension() - 1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, RealVector v) throws MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ setSubVector(index, v.getData());
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, double[] v) throws MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ for (int i = 0; i < v.length; i++) {
+ setEntry(i + index, v[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector add(double[] v) throws IllegalArgumentException {
+ double[] result = v.clone();
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ result[e.getIndex()] += e.getValue();
+ }
+ return new ArrayRealVector(result, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector add(RealVector v) throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ double[] values = ((ArrayRealVector)v).getDataRef();
+ return add(values);
+ }
+ RealVector result = v.copy();
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ final int index = e.getIndex();
+ result.setEntry(index, e.getValue() + result.getEntry(index));
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector subtract(double[] v) throws IllegalArgumentException {
+ double[] result = v.clone();
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ final int index = e.getIndex();
+ result[index] = e.getValue() - result[index];
+ }
+ return new ArrayRealVector(result, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector subtract(RealVector v) throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ double[] values = ((ArrayRealVector)v).getDataRef();
+ return add(values);
+ }
+ RealVector result = v.copy();
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ final int index = e.getIndex();
+ v.setEntry(index, e.getValue() - result.getEntry(index));
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAdd(double d) {
+ return copy().mapAddToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAddToSelf(double d) {
+ if (d != 0) {
+ try {
+ return mapToSelf(BinaryFunction.ADD.fix1stArgument(d));
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public abstract AbstractRealVector copy();
+
+ /** {@inheritDoc} */
+ public double dotProduct(double[] v) throws IllegalArgumentException {
+ return dotProduct(new ArrayRealVector(v, false));
+ }
+
+ /** {@inheritDoc} */
+ public double dotProduct(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ d += e.getValue() * v.getEntry(e.getIndex());
+ }
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector ebeDivide(double[] v) throws IllegalArgumentException {
+ return ebeDivide(new ArrayRealVector(v, false));
+ }
+
+ /** {@inheritDoc} */
+ public RealVector ebeMultiply(double[] v) throws IllegalArgumentException {
+ return ebeMultiply(new ArrayRealVector(v, false));
+ }
+
+ /** {@inheritDoc} */
+ public double getDistance(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ final double diff = e.getValue() - v.getEntry(e.getIndex());
+ d += diff * diff;
+ }
+ return FastMath.sqrt(d);
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() {
+ double sum = 0;
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ final double value = e.getValue();
+ sum += value * value;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /** {@inheritDoc} */
+ public double getL1Norm() {
+ double norm = 0;
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ norm += FastMath.abs(e.getValue());
+ }
+ return norm;
+ }
+
+ /** {@inheritDoc} */
+ public double getLInfNorm() {
+ double norm = 0;
+ Iterator<Entry> it = sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ norm = FastMath.max(norm, FastMath.abs(e.getValue()));
+ }
+ return norm;
+ }
+
+ /** {@inheritDoc} */
+ public double getDistance(double[] v) throws IllegalArgumentException {
+ return getDistance(new ArrayRealVector(v,false));
+ }
+
+ /** {@inheritDoc} */
+ public double getL1Distance(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ d += FastMath.abs(e.getValue() - v.getEntry(e.getIndex()));
+ }
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ public double getL1Distance(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ d += FastMath.abs(e.getValue() - v[e.getIndex()]);
+ }
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ public double getLInfDistance(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ d = FastMath.max(FastMath.abs(e.getValue() - v.getEntry(e.getIndex())), d);
+ }
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ public double getLInfDistance(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ d = FastMath.max(FastMath.abs(e.getValue() - v[e.getIndex()]), d);
+ }
+ return d;
+ }
+
+ /** Get the index of the minimum entry.
+ * @return index of the minimum entry or -1 if vector length is 0
+ * or all entries are 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 value of the minimum entry or NaN if all entries are NaN
+ */
+ public double getMinValue() {
+ final int minIndex = getMinIndex();
+ return minIndex < 0 ? Double.NaN : getEntry(minIndex);
+ }
+
+ /** Get the index of the maximum entry.
+ * @return index of the maximum entry or -1 if vector length is 0
+ * or all entries are 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 value of the maximum entry or NaN if all entries are NaN
+ */
+ public double getMaxValue() {
+ final int maxIndex = getMaxIndex();
+ return maxIndex < 0 ? Double.NaN : getEntry(maxIndex);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAbs() {
+ return copy().mapAbsToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAbsToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.ABS);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAcos() {
+ return copy().mapAcosToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAcosToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.ACOS);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAsin() {
+ return copy().mapAsinToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAsinToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.ASIN);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAtan() {
+ return copy().mapAtanToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapAtanToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.ATAN);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCbrt() {
+ return copy().mapCbrtToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCbrtToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.CBRT);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCeil() {
+ return copy().mapCeilToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCeilToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.CEIL);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCos() {
+ return copy().mapCosToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCosToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.COS);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCosh() {
+ return copy().mapCoshToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapCoshToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.COSH);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapDivide(double d) {
+ return copy().mapDivideToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapDivideToSelf(double d){
+ try {
+ return mapToSelf(BinaryFunction.DIVIDE.fix2ndArgument(d));
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapExp() {
+ return copy().mapExpToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapExpToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.EXP);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapExpm1() {
+ return copy().mapExpm1ToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapExpm1ToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.EXPM1);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapFloor() {
+ return copy().mapFloorToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapFloorToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.FLOOR);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapInv() {
+ return copy().mapInvToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapInvToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.INVERT);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLog() {
+ return copy().mapLogToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLogToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.LOG);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLog10() {
+ return copy().mapLog10ToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLog10ToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.LOG10);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLog1p() {
+ return copy().mapLog1pToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapLog1pToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.LOG1P);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapMultiply(double d) {
+ return copy().mapMultiplyToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapMultiplyToSelf(double d){
+ try {
+ return mapToSelf(BinaryFunction.MULTIPLY.fix1stArgument(d));
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapPow(double d) {
+ return copy().mapPowToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapPowToSelf(double d){
+ try {
+ return mapToSelf(BinaryFunction.POW.fix2ndArgument(d));
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapRint() {
+ return copy().mapRintToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapRintToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.RINT);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSignum() {
+ return copy().mapSignumToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSignumToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.SIGNUM);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSin() {
+ return copy().mapSinToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSinToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.SIN);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSinh() {
+ return copy().mapSinhToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSinhToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.SINH);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSqrt() {
+ return copy().mapSqrtToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSqrtToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.SQRT);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSubtract(double d) {
+ return copy().mapSubtractToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapSubtractToSelf(double d){
+ return mapAddToSelf(-d);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapTan() {
+ return copy().mapTanToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapTanToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.TAN);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapTanh() {
+ return copy().mapTanhToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapTanhToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.TANH);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapUlp() {
+ return copy().mapUlpToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapUlpToSelf() {
+ try {
+ return mapToSelf(ComposableFunction.ULP);
+ } catch (FunctionEvaluationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix outerProduct(RealVector v) throws IllegalArgumentException {
+ RealMatrix product;
+ if (v instanceof SparseRealVector || this instanceof SparseRealVector) {
+ product = new OpenMapRealMatrix(this.getDimension(), v.getDimension());
+ } else {
+ product = new Array2DRowRealMatrix(this.getDimension(), v.getDimension());
+ }
+ Iterator<Entry> thisIt = sparseIterator();
+ Entry thisE = null;
+ while (thisIt.hasNext() && (thisE = thisIt.next()) != null) {
+ Iterator<Entry> otherIt = v.sparseIterator();
+ Entry otherE = null;
+ while (otherIt.hasNext() && (otherE = otherIt.next()) != null) {
+ product.setEntry(thisE.getIndex(), otherE.getIndex(),
+ thisE.getValue() * otherE.getValue());
+ }
+ }
+
+ return product;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
+ return outerProduct(new ArrayRealVector(v, false));
+ }
+
+ /** {@inheritDoc} */
+ public RealVector projection(double[] v) throws IllegalArgumentException {
+ return projection(new ArrayRealVector(v, false));
+ }
+
+ /** {@inheritDoc} */
+ public void set(double value) {
+ Iterator<Entry> it = iterator();
+ Entry e = null;
+ while (it.hasNext() && (e = it.next()) != null) {
+ e.setValue(value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] toArray() {
+ int dim = getDimension();
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = getEntry(i);
+ }
+ return values;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getData() {
+ return toArray();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector unitVector() {
+ RealVector copy = copy();
+ copy.unitize();
+ return copy;
+ }
+
+ /** {@inheritDoc} */
+ public void unitize() {
+ mapDivideToSelf(getNorm());
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<Entry> sparseIterator() {
+ return new SparseEntryIterator();
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<Entry> iterator() {
+ final int dim = getDimension();
+ return new Iterator<Entry>() {
+
+ /** Current index. */
+ private int i = 0;
+
+ /** Current entry. */
+ private EntryImpl e = new EntryImpl();
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return i < dim;
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ e.setIndex(i++);
+ return e;
+ }
+
+ /** {@inheritDoc} */
+ public void remove() {
+ throw new MathUnsupportedOperationException();
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ public RealVector map(UnivariateRealFunction function) throws FunctionEvaluationException {
+ return copy().mapToSelf(function);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector mapToSelf(UnivariateRealFunction function) throws FunctionEvaluationException {
+ Iterator<Entry> it = (function.value(0) == 0) ? sparseIterator() : iterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ e.setValue(function.value(e.getValue()));
+ }
+ return this;
+ }
+
+ /** An entry in the vector. */
+ protected class EntryImpl extends Entry {
+
+ /** Simple constructor. */
+ public EntryImpl() {
+ setIndex(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getValue() {
+ return getEntry(getIndex());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setValue(double newValue) {
+ setEntry(getIndex(), newValue);
+ }
+ }
+
+ /**
+ * This class should rare be used, but is here to provide
+ * a default implementation of sparseIterator(), which is implemented
+ * by walking over the entries, skipping those whose values are the default one.
+ *
+ * Concrete subclasses which are SparseVector implementations should
+ * make their own sparse iterator, not use this one.
+ *
+ * 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 EntryImpl current;
+
+ /** Next entry for {@link #next()} to return. */
+ private EntryImpl next;
+
+ /** Simple constructor. */
+ protected SparseEntryIterator() {
+ dim = getDimension();
+ current = new EntryImpl();
+ next = new EntryImpl();
+ if (next.getValue() == 0) {
+ advance(next);
+ }
+ }
+
+ /** Advance an entry up to the next nonzero one.
+ * @param e entry to advance
+ */
+ protected void advance(EntryImpl 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} */
+ public void remove() {
+ throw new MathUnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/AnyMatrix.java b/src/main/java/org/apache/commons/math/linear/AnyMatrix.java
new file mode 100644
index 0000000..f435e2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/AnyMatrix.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.math.linear;
+
+
+/**
+ * Interface defining very basic matrix operations.
+ * @version $Revision: 772119 $ $Date: 2009-05-06 11:43:28 +0200 (mer. 06 mai 2009) $
+ * @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/math/linear/Array2DRowFieldMatrix.java b/src/main/java/org/apache/commons/math/linear/Array2DRowFieldMatrix.java
new file mode 100644
index 0000000..fdbe24b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/Array2DRowFieldMatrix.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.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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.</li></ul>
+ * </p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+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 */
+ protected 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 FieldMatrix<T> with the supplied row and column dimensions.
+ *
+ * @param field field to which the elements belong
+ * @param rowDimension the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @throws IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public Array2DRowFieldMatrix(final Field<T> field,
+ final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ super(field, rowDimension, columnDimension);
+ data = buildArray(field, rowDimension, columnDimension);
+ }
+
+ /**
+ * Create a new 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</code>.</p>
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
+ */
+ public Array2DRowFieldMatrix(final T[][] d)
+ throws IllegalArgumentException, NullPointerException {
+ super(extractField(d));
+ copyIn(d);
+ }
+
+ /**
+ * Create a new 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
+ * FieldMatrix<T> and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param d data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #Array2DRowFieldMatrix(FieldElement[][])
+ */
+ public Array2DRowFieldMatrix(final T[][] d, final boolean copyArray)
+ throws IllegalArgumentException, NullPointerException {
+ super(extractField(d));
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ if (d == null) {
+ throw new NullPointerException();
+ }
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length);
+ }
+ }
+ data = d;
+ }
+ }
+
+ /**
+ * Create a new (column) FieldMatrix<T> using <code>v</code> as the
+ * data for the unique column of the <code>v.length x 1</code> matrix
+ * created.
+ * <p>The input array is copied, not referenced.</p>
+ *
+ * @param v column vector holding data for new matrix
+ */
+ public Array2DRowFieldMatrix(final T[] v) {
+ super(extractField(v));
+ final int nRows = v.length;
+ data = 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 IllegalArgumentException {
+ return new Array2DRowFieldMatrix<T>(getField(), rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> copy() {
+ return new Array2DRowFieldMatrix<T>(copyOut(), false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> add(final FieldMatrix<T> m)
+ throws IllegalArgumentException {
+ try {
+ return add((Array2DRowFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+ return super.add(m);
+ }
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public Array2DRowFieldMatrix<T> add(final Array2DRowFieldMatrix<T> m)
+ throws IllegalArgumentException {
+
+ // safety check
+ checkAdditionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final T[][] outData = 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>(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> subtract(final FieldMatrix<T> m)
+ throws IllegalArgumentException {
+ try {
+ return subtract((Array2DRowFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+ return super.subtract(m);
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public Array2DRowFieldMatrix<T> subtract(final Array2DRowFieldMatrix<T> m)
+ throws IllegalArgumentException {
+
+ // safety check
+ checkSubtractionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final T[][] outData = 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>(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> multiply(final FieldMatrix<T> m)
+ throws IllegalArgumentException {
+ try {
+ return multiply((Array2DRowFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+ return super.multiply(m);
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by <code>m</code>.
+ * @param m matrix to postmultiply by
+ * @return this*m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public Array2DRowFieldMatrix<T> multiply(final Array2DRowFieldMatrix<T> m)
+ throws IllegalArgumentException {
+
+ // safety check
+ checkMultiplicationCompatible(m);
+
+ final int nRows = this.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = this.getColumnDimension();
+ final T[][] outData = 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>(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ * <p>
+ * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
+ *
+ * @return 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 MatrixIndexException {
+ if (data == null) {
+ if (row > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
+ }
+ if (column > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
+ }
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ data = buildArray(getField(), subMatrix.length, nCols);
+ for (int i = 0; i < data.length; ++i) {
+ if (subMatrix[i].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS, 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 MatrixIndexException {
+ try {
+ return data[row][column];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final T value)
+ throws MatrixIndexException {
+ try {
+ data[row][column] = value;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final T increment)
+ throws MatrixIndexException {
+ try {
+ data[row][column] = data[row][column].add(increment);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final T factor)
+ throws MatrixIndexException {
+ try {
+ data[row][column] = data[row][column].multiply(factor);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@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 IllegalArgumentException {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (v.length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nCols);
+ }
+ final T[] out = 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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nRows);
+ }
+
+ final T[] out = 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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();
+ }
+
+ /**
+ * Returns 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 = 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;
+ }
+
+ /**
+ * Replaces data with a fresh copy of the input array.
+ * <p>
+ * Verifies that the input array is rectangular and non-empty.</p>
+ *
+ * @param in data to copy in
+ * @throws IllegalArgumentException if input array is empty or not
+ * rectangular
+ * @throws NullPointerException if input array is null
+ */
+ private void copyIn(final T[][] in) {
+ setSubMatrix(in, 0, 0);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/Array2DRowRealMatrix.java b/src/main/java/org/apache/commons/math/linear/Array2DRowRealMatrix.java
new file mode 100644
index 0000000..5034a47
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/Array2DRowRealMatrix.java
@@ -0,0 +1,621 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Implementation of RealMatrix using a double[][] array to store entries and
+ * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
+ * LU decomposition</a> to support linear system
+ * solution and inverse.
+ * <p>
+ * The LU decomposition is performed as needed, to support the following operations: <ul>
+ * <li>solve</li>
+ * <li>isSingular</li>
+ * <li>getDeterminant</li>
+ * <li>inverse</li> </ul></p>
+ * <p>
+ * <strong>Usage notes</strong>:<br>
+ * <ul><li>
+ * The LU decomposition is cached and reused on subsequent calls.
+ * If data are modified via references to the underlying array obtained using
+ * <code>getDataRef()</code>, then the stored LU decomposition will not be
+ * discarded. In this case, you need to explicitly invoke
+ * <code>LUDecompose()</code> to recompute the decomposition
+ * before using any of the methods above.</li>
+ * <li>
+ * As specified in the {@link RealMatrix} 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.</li></ul>
+ * </p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class Array2DRowRealMatrix extends AbstractRealMatrix implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1067294169172445528L;
+
+ /** Entries of the matrix */
+ protected double data[][];
+
+ /**
+ * Creates a matrix with no data
+ */
+ public Array2DRowRealMatrix() {
+ }
+
+ /**
+ * 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 IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public Array2DRowRealMatrix(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ super(rowDimension, columnDimension);
+ data = new double[rowDimension][columnDimension];
+ }
+
+ /**
+ * Create a new 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</code>.</p>
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #Array2DRowRealMatrix(double[][], boolean)
+ */
+ public Array2DRowRealMatrix(final double[][] d)
+ throws IllegalArgumentException, NullPointerException {
+ copyIn(d);
+ }
+
+ /**
+ * Create a new RealMatrix using the input array as the underlying
+ * data array.
+ * <p>If an array is built specially in order to be embedded in a
+ * RealMatrix and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param d data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #Array2DRowRealMatrix(double[][])
+ */
+ public Array2DRowRealMatrix(final double[][] d, final boolean copyArray)
+ throws IllegalArgumentException, NullPointerException {
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ if (d == null) {
+ throw new NullPointerException();
+ }
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length);
+ }
+ }
+ data = d;
+ }
+ }
+
+ /**
+ * Create a new (column) RealMatrix using <code>v</code> as the
+ * data for the unique column of the <code>v.length x 1</code> matrix
+ * created.
+ * <p>The input array is copied, not referenced.</p>
+ *
+ * @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 IllegalArgumentException {
+ return new Array2DRowRealMatrix(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix copy() {
+ return new Array2DRowRealMatrix(copyOut(), false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix add(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return add((Array2DRowRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return super.add(m);
+ }
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public Array2DRowRealMatrix add(final Array2DRowRealMatrix m)
+ throws IllegalArgumentException {
+
+ // 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);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix subtract(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return subtract((Array2DRowRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return super.subtract(m);
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public Array2DRowRealMatrix subtract(final Array2DRowRealMatrix m)
+ throws IllegalArgumentException {
+
+ // safety check
+ 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);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix multiply(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return multiply((Array2DRowRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return super.multiply(m);
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by <code>m</code>.
+ * @param m matrix to postmultiply by
+ * @return this*m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m)
+ throws IllegalArgumentException {
+
+ // safety check
+ 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];
+ for (int row = 0; row < nRows; row++) {
+ final double[] dataRow = data[row];
+ final double[] outDataRow = outData[row];
+ for (int col = 0; col < nCols; col++) {
+ double sum = 0;
+ for (int i = 0; i < nSum; i++) {
+ sum += dataRow[i] * m.data[i][col];
+ }
+ outDataRow[col] = sum;
+ }
+ }
+
+ return new Array2DRowRealMatrix(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ * <p>
+ * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
+ *
+ * @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 MatrixIndexException {
+ if (data == null) {
+ if (row > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
+ }
+ if (column > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
+ }
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, subMatrix[i].length);
+ }
+ 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 MatrixIndexException {
+ try {
+ return data[row][column];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws MatrixIndexException {
+ try {
+ data[row][column] = value;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws MatrixIndexException {
+ try {
+ data[row][column] += increment;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws MatrixIndexException {
+ try {
+ data[row][column] *= factor;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@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 IllegalArgumentException {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (v.length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, 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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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();
+ }
+
+ /**
+ * Returns 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;
+ }
+
+ /**
+ * Replaces data with a fresh copy of the input array.
+ * <p>
+ * Verifies that the input array is rectangular and non-empty.</p>
+ *
+ * @param in data to copy in
+ * @throws IllegalArgumentException if input array is empty or not
+ * rectangular
+ * @throws NullPointerException if input array is null
+ */
+ private void copyIn(final double[][] in) {
+ setSubMatrix(in, 0, 0);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/ArrayFieldVector.java b/src/main/java/org/apache/commons/math/linear/ArrayFieldVector.java
new file mode 100644
index 0000000..eefce2f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/ArrayFieldVector.java
@@ -0,0 +1,869 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * This class implements the {@link FieldVector} interface with a {@link FieldElement} array.
+ * @param <T> the type of the field elements
+ * @version $Revision: 1003997 $ $Date: 2010-10-03 18:45:55 +0200 (dim. 03 oct. 2010) $
+ * @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. */
+ protected T[] data;
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /**
+ * Build a 0-length vector.
+ * <p>Zero-length vectors may be used to initialized 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</code> methods ({@link #append(FieldElement[])},
+ * {@link #add(FieldVector)}, {@link #append(ArrayFieldVector)}) to gather data
+ * into this vector.</p>
+ * @param field field to which the elements belong
+ */
+ public ArrayFieldVector(final Field<T> field) {
+ this(field, 0);
+ }
+
+ /**
+ * Construct a (size)-length vector of zeros.
+ * @param field field to which the elements belong
+ * @param size size of the vector
+ */
+ public ArrayFieldVector(Field<T> field, int size) {
+ this.field = field;
+ data = buildArray(size);
+ Arrays.fill(data, field.getZero());
+ }
+
+ /**
+ * Construct an (size)-length vector with preset values.
+ * @param size size of the vector
+ * @param preset fill the vector with this scalar 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.
+ * <p>
+ * 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.
+ * </p>
+ * @param d array of Ts.
+ * @throws IllegalArgumentException if <code>d</code> is empty
+ * @see #ArrayFieldVector(Field, FieldElement[])
+ */
+ public ArrayFieldVector(T[] d)
+ throws IllegalArgumentException {
+ try {
+ field = d[0].getField();
+ data = d.clone();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 of Ts.
+ * @see #ArrayFieldVector(FieldElement[])
+ */
+ public ArrayFieldVector(Field<T> field, T[] d) {
+ this.field = field;
+ data = d.clone();
+ }
+
+ /**
+ * Create a new ArrayFieldVector using the input array as the underlying
+ * data array.
+ * <p>If an array is built specially in order to be embedded in a
+ * ArrayFieldVector and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * <p>
+ * 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.
+ * </p>
+ * @param d data for new vector
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @throws IllegalArgumentException if <code>d</code> is empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #ArrayFieldVector(FieldElement[])
+ * @see #ArrayFieldVector(Field, FieldElement[], boolean)
+ */
+ public ArrayFieldVector(T[] d, boolean copyArray)
+ throws NullPointerException, IllegalArgumentException {
+ if (d.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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.
+ * <p>If an array is built specially in order to be embedded in a
+ * ArrayFieldVector and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param field field to which the elements belong
+ * @param d data for new vector
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @see #ArrayFieldVector(FieldElement[], boolean)
+ */
+ public ArrayFieldVector(Field<T> field, T[] d, boolean copyArray) {
+ this.field = field;
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * Construct a vector from part of a array.
+ * @param d array of Ts.
+ * @param pos position of first entry
+ * @param size number of entries to copy
+ */
+ public ArrayFieldVector(T[] d, int pos, int size) {
+ if (d.length < pos + size) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POSITION_SIZE_MISMATCH_INPUT_ARRAY,
+ pos, size, d.length);
+ }
+ field = d[0].getField();
+ data = buildArray(size);
+ System.arraycopy(d, pos, data, 0, size);
+ }
+
+ /**
+ * Construct a vector from another vector, using a deep copy.
+ * @param v vector to copy
+ */
+ public ArrayFieldVector(FieldVector<T> v) {
+ field = v.getField();
+ data = buildArray(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
+ */
+ public ArrayFieldVector(ArrayFieldVector<T> v) {
+ field = v.getField();
+ data = v.data.clone();
+ }
+
+ /**
+ * Construct a vector from another vector.
+ * @param v vector to copy
+ * @param deep if true perform a deep copy otherwise perform a shallow copy
+ */
+ public ArrayFieldVector(ArrayFieldVector<T> v, boolean deep) {
+ 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)
+ */
+ public ArrayFieldVector(ArrayFieldVector<T> v1, ArrayFieldVector<T> v2) {
+ field = v1.getField();
+ data = buildArray(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 ArrayFieldVector(ArrayFieldVector<T> v1, T[] v2) {
+ field = v1.getField();
+ data = buildArray(v1.data.length + v2.length);
+ System.arraycopy(v1.data, 0, data, 0, v1.data.length);
+ System.arraycopy(v2, 0, data, v1.data.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)
+ */
+ public ArrayFieldVector(T[] v1, ArrayFieldVector<T> v2) {
+ field = v2.getField();
+ data = buildArray(v1.length + v2.data.length);
+ System.arraycopy(v1, 0, data, 0, v1.length);
+ System.arraycopy(v2.data, 0, data, v1.length, v2.data.length);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ * <p>
+ * 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.
+ * </p>
+ * @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)
+ * @exception IllegalArgumentException if both vectors are empty
+ * @see #ArrayFieldVector(Field, FieldElement[], FieldElement[])
+ */
+ public ArrayFieldVector(T[] v1, T[] v2) {
+ try {
+ data = buildArray(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();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ }
+
+ /**
+ * 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)
+ * @see #ArrayFieldVector(FieldElement[], FieldElement[])
+ */
+ public ArrayFieldVector(Field<T> field, T[] v1, T[] v2) {
+ if (v1.length + v2.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ data = buildArray(v1.length + v2.length);
+ System.arraycopy(v1, 0, data, 0, v1.length);
+ System.arraycopy(v2, 0, data, v1.length, v2.length);
+ this.field = data[0].getField();
+ }
+
+ /** 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.getZero().getClass(), length);
+ }
+
+ /** {@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 IllegalArgumentException {
+ try {
+ return add((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> add(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(v[i]);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /**
+ * Compute the sum of this and v.
+ * @param v vector to be added
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayFieldVector<T> add(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return (ArrayFieldVector<T>) add(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> subtract(FieldVector<T> v) throws IllegalArgumentException {
+ try {
+ return subtract((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> subtract(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(v[i]);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /**
+ * Compute this minus v.
+ * @param v vector to be subtracted
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayFieldVector<T> subtract(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return (ArrayFieldVector<T>) subtract(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAdd(T d) {
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(d);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAddToSelf(T d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].add(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtract(T d) {
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(d);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtractToSelf(T d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].subtract(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiply(T d) {
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(d);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiplyToSelf(T d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].multiply(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivide(T d) {
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].divide(d);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivideToSelf(T d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].divide(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInv() {
+ T[] out = buildArray(data.length);
+ final T one = field.getOne();
+ for (int i = 0; i < data.length; i++) {
+ out[i] = one.divide(data[i]);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInvToSelf() {
+ final T one = field.getOne();
+ for (int i = 0; i < data.length; i++) {
+ data[i] = one.divide(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeMultiply(FieldVector<T> v)
+ throws IllegalArgumentException {
+ try {
+ return ebeMultiply((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeMultiply(T[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(v[i]);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /**
+ * 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
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayFieldVector<T> ebeMultiply(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return (ArrayFieldVector<T>) ebeMultiply(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeDivide(FieldVector<T> v)
+ throws IllegalArgumentException {
+ try {
+ return ebeDivide((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].divide(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeDivide(T[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T[] out = buildArray(data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].divide(v[i]);
+ }
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayFieldVector<T> ebeDivide(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return (ArrayFieldVector<T>) ebeDivide(v.data);
+ }
+
+ /** {@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.</p>
+ * @return array of entries
+ */
+ public T[] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public T dotProduct(FieldVector<T> v)
+ throws IllegalArgumentException {
+ 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;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T dotProduct(T[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T dot = field.getZero();
+ for (int i = 0; i < data.length; i++) {
+ dot = dot.add(data[i].multiply(v[i]));
+ }
+ return dot;
+ }
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public T dotProduct(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return dotProduct(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(FieldVector<T> v) {
+ return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(T[] v) {
+ return projection(new ArrayFieldVector<T>(v, false));
+ }
+
+ /** 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 v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayFieldVector<T> projection(ArrayFieldVector<T> v) {
+ return (ArrayFieldVector<T>) v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> outerProduct(FieldVector<T> v)
+ throws IllegalArgumentException {
+ try {
+ return outerProduct((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ final int m = data.length;
+ final FieldMatrix<T> out = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < data.length; 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 square matrix outer product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public FieldMatrix<T> outerProduct(ArrayFieldVector<T> v)
+ throws IllegalArgumentException {
+ return outerProduct(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> outerProduct(T[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ final int m = data.length;
+ final FieldMatrix<T> out = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < data.length; j++) {
+ out.setEntry(i, j, data[i].multiply(v[j]));
+ }
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public T getEntry(int index) throws MatrixIndexException {
+ 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 = buildArray(data.length + 1);
+ System.arraycopy(data, 0, out, 0, data.length);
+ out[data.length] = in;
+ return new ArrayFieldVector<T>(out);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(T[] in) {
+ return new ArrayFieldVector<T>(this, in);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getSubVector(int index, int 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) {
+ 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);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, T[] v) {
+ try {
+ System.arraycopy(v, 0, data, index, v.length);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ }
+ }
+
+ /**
+ * Set a set of consecutive elements.
+ *
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ */
+ public void set(int index, ArrayFieldVector<T> v)
+ throws MatrixIndexException {
+ setSubVector(index, v.data);
+ }
+
+ /** {@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 IllegalArgumentException if the vectors do not
+ * have the same dimension
+ */
+ protected void checkVectorDimensions(FieldVector<T> v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n expected dimension.
+ * @exception IllegalArgumentException if the dimension is
+ * inconsistent with vector size
+ */
+ protected void checkVectorDimensions(int n)
+ throws IllegalArgumentException {
+ if (data.length != n) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ data.length, n);
+ }
+ }
+
+ /**
+ * Test for the equality of two real vectors.
+ * <p>
+ * If all coordinates of two real vectors are exactly the same, and none are
+ * <code>Double.NaN</code>, the two real 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
+ * real vector are equal to <code>Double.NaN</code>, the real vector is equal to
+ * a vector with all <code>Double.NaN</code> coordinates.
+ * </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 == 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.</p>
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ int h = 3542;
+ for (final T a : data) {
+ h = h ^ a.hashCode();
+ }
+ return h;
+ }
+
+ /**
+ * Check if an index is valid.
+ * @param index index to check
+ * @exception MatrixIndexException if index is not valid
+ */
+ private void checkIndex(final int index)
+ throws MatrixIndexException {
+ if (index < 0 || index >= getDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.INDEX_OUT_OF_RANGE,
+ index, 0, getDimension() - 1);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/ArrayRealVector.java b/src/main/java/org/apache/commons/math/linear/ArrayRealVector.java
new file mode 100644
index 0000000..9f15a6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/ArrayRealVector.java
@@ -0,0 +1,1228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class implements the {@link RealVector} interface with a double array.
+ * @version $Revision: 1003993 $ $Date: 2010-10-03 18:39:16 +0200 (dim. 03 oct. 2010) $
+ * @since 2.0
+ */
+public class ArrayRealVector extends AbstractRealVector 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. */
+ protected double data[];
+
+ /**
+ * Build a 0-length vector.
+ * <p>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</code> method ({@link #append(double)}, {@link
+ * #append(double[])}, {@link #append(ArrayRealVector)}) to gather data
+ * into this vector.</p>
+ */
+ public ArrayRealVector() {
+ data = new double[0];
+ }
+
+ /**
+ * Construct a (size)-length vector of zeros.
+ * @param size size of the vector
+ */
+ public ArrayRealVector(int size) {
+ data = new double[size];
+ }
+
+ /**
+ * Construct an (size)-length vector with preset values.
+ * @param size size of the vector
+ * @param preset fill the vector with this scalar 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 of doubles.
+ */
+ public ArrayRealVector(double[] d) {
+ data = d.clone();
+ }
+
+ /**
+ * Create a new ArrayRealVector using the input array as the underlying
+ * data array.
+ * <p>If an array is built specially in order to be embedded in a
+ * ArrayRealVector and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param d data for new vector
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @see #ArrayRealVector(double[])
+ */
+ public ArrayRealVector(double[] d, boolean copyArray) {
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * Construct a vector from part of a array.
+ * @param d array of doubles.
+ * @param pos position of first entry
+ * @param size number of entries to copy
+ */
+ public ArrayRealVector(double[] d, int pos, int size) {
+ if (d.length < pos + size) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POSITION_SIZE_MISMATCH_INPUT_ARRAY, pos, size, d.length);
+ }
+ data = new double[size];
+ System.arraycopy(d, pos, data, 0, size);
+ }
+
+ /**
+ * Construct a vector from an array.
+ * @param d array of Doubles.
+ */
+ 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 a Double array
+ * @param d array of Doubles.
+ * @param pos position of first entry
+ * @param size number of entries to copy
+ */
+ public ArrayRealVector(Double[] d, int pos, int size) {
+ if (d.length < pos + size) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POSITION_SIZE_MISMATCH_INPUT_ARRAY, pos, size, d.length);
+ }
+ 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
+ */
+ public ArrayRealVector(RealVector v) {
+ 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
+ */
+ public ArrayRealVector(ArrayRealVector v) {
+ this(v, true);
+ }
+
+ /**
+ * Construct a vector from another vector.
+ * @param v vector to copy
+ * @param deep if 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 AbstractRealVector copy() {
+ return new ArrayRealVector(this, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector add(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return add((ArrayRealVector) v);
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ Iterator<Entry> it = v.sparseIterator();
+ Entry e;
+ while (it.hasNext() && (e = it.next()) != null) {
+ out[e.getIndex()] += e.getValue();
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector add(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] += v[i];
+ }
+ return new ArrayRealVector(out, false);
+ }
+
+ /**
+ * Compute the sum of this and v.
+ * @param v vector to be added
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayRealVector add(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return (ArrayRealVector) add(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector subtract(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return subtract((ArrayRealVector) v);
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ Iterator<Entry> it = v.sparseIterator();
+ Entry e;
+ while(it.hasNext() && (e = it.next()) != null) {
+ out[e.getIndex()] -= e.getValue();
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector subtract(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] -= v[i];
+ }
+ return new ArrayRealVector(out, false);
+ }
+
+ /**
+ * Compute this minus v.
+ * @param v vector to be subtracted
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayRealVector subtract(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return (ArrayRealVector) subtract(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAddToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i] + d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSubtractToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i] - d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapMultiplyToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i] * d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapDivideToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i] / d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapPowToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.pow(data[i], d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapExpToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.exp(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapExpm1ToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.expm1(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapLogToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.log(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapLog10ToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.log10(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapLog1pToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.log1p(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapCoshToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.cosh(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSinhToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.sinh(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapTanhToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.tanh(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapCosToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.cos(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSinToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.sin(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapTanToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.tan(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAcosToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.acos(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAsinToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.asin(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAtanToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.atan(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapInvToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = 1.0 / data[i];
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAbsToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.abs(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSqrtToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.sqrt(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapCbrtToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.cbrt(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapCeilToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.ceil(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapFloorToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.floor(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapRintToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.rint(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSignumToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.signum(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapUlpToSelf() {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = FastMath.ulp(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector ebeMultiply(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return ebeMultiply((ArrayRealVector) v);
+ } 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 RealVector ebeMultiply(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] *= v[i];
+ }
+ return new ArrayRealVector(out, false);
+ }
+
+ /**
+ * 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
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayRealVector ebeMultiply(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return (ArrayRealVector) ebeMultiply(v.data);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector ebeDivide(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return ebeDivide((ArrayRealVector) v);
+ } 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 RealVector ebeDivide(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] /= v[i];
+ }
+ return new ArrayRealVector(out, false);
+ }
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayRealVector ebeDivide(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return (ArrayRealVector) ebeDivide(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getData() {
+ return data.clone();
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ * <p>Does not make a fresh copy of the underlying data.</p>
+ * @return array of entries
+ */
+ public double[] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double dotProduct(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return dotProduct((ArrayRealVector) v);
+ } else {
+ checkVectorDimensions(v);
+ double dot = 0;
+ Iterator<Entry> it = v.sparseIterator();
+ Entry e;
+ while(it.hasNext() && (e = it.next()) != null) {
+ dot += data[e.getIndex()] * e.getValue();
+ }
+ return dot;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double dotProduct(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * v[i];
+ }
+ return dot;
+ }
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public double dotProduct(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return dotProduct(v.data);
+ }
+
+ /** {@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 IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return getDistance((ArrayRealVector) v);
+ } 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 getDistance(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v[i];
+ sum += delta * delta;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /**
+ * 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
+ * elements differences, or euclidian distance.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @exception IllegalArgumentException if v is not the same size as this
+ * @see #getDistance(RealVector)
+ * @see #getL1Distance(ArrayRealVector)
+ * @see #getLInfDistance(ArrayRealVector)
+ * @see #getNorm()
+ */
+ public double getDistance(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return getDistance(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Distance(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return getL1Distance((ArrayRealVector) v);
+ } 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 getL1Distance(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v[i];
+ sum += FastMath.abs(delta);
+ }
+ return sum;
+ }
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @exception IllegalArgumentException if v is not the same size as this
+ * @see #getDistance(RealVector)
+ * @see #getL1Distance(ArrayRealVector)
+ * @see #getLInfDistance(ArrayRealVector)
+ * @see #getNorm()
+ */
+ public double getL1Distance(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return getL1Distance(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return getLInfDistance((ArrayRealVector) v);
+ } 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 double getLInfDistance(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double max = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v[i];
+ max = FastMath.max(max, FastMath.abs(delta));
+ }
+ return max;
+ }
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @exception IllegalArgumentException if v is not the same size as this
+ * @see #getDistance(RealVector)
+ * @see #getL1Distance(ArrayRealVector)
+ * @see #getLInfDistance(ArrayRealVector)
+ * @see #getNorm()
+ */
+ public double getLInfDistance(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return getLInfDistance(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector unitVector() throws ArithmeticException {
+ final double norm = getNorm();
+ if (norm == 0) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ return mapDivide(norm);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unitize() throws ArithmeticException {
+ final double norm = getNorm();
+ if (norm == 0) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ mapDivideToSelf(norm);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector projection(RealVector v) {
+ return v.mapMultiply(dotProduct(v) / v.dotProduct(v));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector projection(double[] v) {
+ return projection(new ArrayRealVector(v, false));
+ }
+
+ /** 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 v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ public ArrayRealVector projection(ArrayRealVector v) {
+ return (ArrayRealVector) v.mapMultiply(dotProduct(v) / v.dotProduct(v));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix outerProduct(RealVector v)
+ throws IllegalArgumentException {
+ if (v instanceof ArrayRealVector) {
+ return outerProduct((ArrayRealVector) v);
+ } else {
+ checkVectorDimensions(v);
+ final int m = data.length;
+ final RealMatrix out = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < data.length; j++) {
+ out.setEntry(i, j, data[i] * v.getEntry(j));
+ }
+ }
+ return out;
+ }
+ }
+
+ /**
+ * Compute the outer product.
+ * @param v vector with which outer product should be computed
+ * @return the square matrix outer product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ public RealMatrix outerProduct(ArrayRealVector v)
+ throws IllegalArgumentException {
+ return outerProduct(v.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix outerProduct(double[] v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ final int m = data.length;
+ final RealMatrix out = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < data.length; j++) {
+ out.setEntry(i, j, data[i] * v[j]);
+ }
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public double getEntry(int index) throws MatrixIndexException {
+ return data[index];
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return data.length;
+ }
+
+ /** {@inheritDoc} */
+ 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} */
+ 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} */
+ public RealVector append(double[] in) {
+ return new ArrayRealVector(this, in);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getSubVector(int index, int 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} */
+ public void setEntry(int index, double value) {
+ try {
+ data[index] = value;
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubVector(int index, RealVector v) {
+ try {
+ try {
+ set(index, (ArrayRealVector) 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);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubVector(int index, double[] v) {
+ try {
+ System.arraycopy(v, 0, data, index, v.length);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ }
+ }
+
+ /**
+ * Set a set of consecutive elements.
+ *
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ */
+ public void set(int index, ArrayRealVector v)
+ throws MatrixIndexException {
+ setSubVector(index, v.data);
+ }
+
+ /** {@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
+ * @exception IllegalArgumentException if the vectors do not
+ * have the same dimension
+ */
+ @Override
+ protected void checkVectorDimensions(RealVector v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n expected dimension.
+ * @exception IllegalArgumentException if the dimension is
+ * inconsistent with vector size
+ */
+ @Override
+ protected void checkVectorDimensions(int n)
+ throws IllegalArgumentException {
+ if (data.length != n) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ data.length, n);
+ }
+ }
+
+ /**
+ * 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() {
+ for (double v : data) {
+ if (Double.isNaN(v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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() {
+
+ if (isNaN()) {
+ return false;
+ }
+
+ for (double v : data) {
+ if (Double.isInfinite(v)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Test for the equality of two real vectors.
+ * <p>
+ * If all coordinates of two real vectors are exactly the same, and none are
+ * <code>Double.NaN</code>, the two real 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
+ * real vector are equal to <code>Double.NaN</code>, the real vector is equal to
+ * a vector with all <code>Double.NaN</code> coordinates.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two vector objects are equal, false if
+ * object is null, not an instance of RealVector, or
+ * not equal to this RealVector instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || !(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;
+ }
+
+ /**
+ * Get a hashCode for the real 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 9;
+ }
+ return MathUtils.hash(data);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/BiDiagonalTransformer.java b/src/main/java/org/apache/commons/math/linear/BiDiagonalTransformer.java
new file mode 100644
index 0000000..78230de
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/BiDiagonalTransformer.java
@@ -0,0 +1,381 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.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>
+ * <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.</p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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.
+ */
+ public 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.</p>
+ * @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;
+ cachedU = MatrixUtils.createRealMatrix(m, m);
+
+ // fill up the part of the matrix not affected by Householder transforms
+ for (int k = m - 1; k >= p; --k) {
+ cachedU.setEntry(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];
+ cachedU.setEntry(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 -= cachedU.getEntry(i, j) * householderVectors[i][k - diagOffset];
+ }
+ alpha /= diagonal[k - diagOffset] * hK[k - diagOffset];
+
+ for (int i = k; i < m; ++i) {
+ cachedU.addToEntry(i, j, -alpha * householderVectors[i][k - diagOffset]);
+ }
+ }
+ }
+ }
+ if (diagOffset > 0) {
+ cachedU.setEntry(0, 0, 1);
+ }
+
+ }
+
+ // 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;
+ cachedB = MatrixUtils.createRealMatrix(m, n);
+ for (int i = 0; i < main.length; ++i) {
+ cachedB.setEntry(i, i, main[i]);
+ if (m < n) {
+ if (i > 0) {
+ cachedB.setEntry(i, i - 1, secondary[i - 1]);
+ }
+ } else {
+ if (i < main.length - 1) {
+ cachedB.setEntry(i, i + 1, secondary[i]);
+ }
+ }
+ }
+
+ }
+
+ // 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.</p>
+ * @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;
+ cachedV = MatrixUtils.createRealMatrix(n, n);
+
+ // fill up the part of the matrix not affected by Householder transforms
+ for (int k = n - 1; k >= p; --k) {
+ cachedV.setEntry(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];
+ cachedV.setEntry(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 -= cachedV.getEntry(i, j) * hK[i];
+ }
+ beta /= diagonal[k - diagOffset] * hK[k];
+
+ for (int i = k; i < n; ++i) {
+ cachedV.addToEntry(i, j, -beta * hK[i]);
+ }
+ }
+ }
+ }
+ if (diagOffset > 0) {
+ cachedV.setEntry(0, 0, 1);
+ }
+
+ }
+
+ // 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.</p>
+ * @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.</p>
+ * @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.</p>
+ * @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.</p>
+ */
+ 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.</p>
+ */
+ 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/math/linear/BigMatrix.java b/src/main/java/org/apache/commons/math/linear/BigMatrix.java
new file mode 100644
index 0000000..5ea3d3e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/BigMatrix.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.math.BigDecimal;
+
+/**
+ * Interface defining a real-valued matrix with basic algebraic operations, using
+ * BigDecimal representations for the entries.
+ * <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.</p>
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @deprecated as of 2.0, replaced by {@link FieldMatrix} with a {@link
+ * org.apache.commons.math.util.BigReal} parameter
+ */
+@Deprecated
+public interface BigMatrix extends AnyMatrix {
+
+ /**
+ * Returns a (deep) copy of this.
+ *
+ * @return matrix copy
+ */
+ BigMatrix copy();
+
+ /**
+ * Compute the sum of this and m.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @exception IllegalArgumentException if m is not the same size as this
+ */
+ BigMatrix add(BigMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Compute this minus m.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @exception IllegalArgumentException if m is not the same size as this
+ */
+ BigMatrix subtract(BigMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result of adding d to each entry of this.
+ *
+ * @param d value to be added to each entry
+ * @return d + this
+ */
+ BigMatrix scalarAdd(BigDecimal d);
+
+ /**
+ * Returns the result multiplying each entry of this by d.
+ *
+ * @param d value to multiply all entries by
+ * @return d * this
+ */
+ BigMatrix scalarMultiply(BigDecimal d);
+
+ /**
+ * Returns the result of postmultiplying this by m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ BigMatrix multiply(BigMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result premultiplying this by <code>m</code>.
+ * @param m matrix to premultiply by
+ * @return m * this
+ * @throws IllegalArgumentException
+ * if rowDimension(this) != columnDimension(m)
+ */
+ BigMatrix preMultiply(BigMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ *
+ * @return 2-dimensional array of entries
+ */
+ BigDecimal[][] getData();
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ *
+ * @return 2-dimensional array of entries
+ */
+ double [][] getDataAsDoubleArray();
+
+ /***
+ * Gets the rounding mode
+ * @return the rounding mode
+ */
+ int getRoundingMode();
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html">
+ * maximum absolute row sum norm</a> of the matrix.
+ *
+ * @return norm
+ */
+ BigDecimal getNorm();
+
+ /**
+ * Gets a submatrix. 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
+ * @return The subMatrix containing the data of the
+ * specified rows and columns
+ * @exception MatrixIndexException if the indices are not valid
+ */
+ BigMatrix getSubMatrix(int startRow, int endRow, int startColumn,
+ int endColumn) throws MatrixIndexException;
+
+ /**
+ * 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
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ BigMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws MatrixIndexException;
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row matrix
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ BigMatrix getRowMatrix(int row) throws MatrixIndexException;
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a column matrix. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column matrix
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ BigMatrix getColumnMatrix(int column) throws MatrixIndexException;
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ BigDecimal[] getRow(int row) throws MatrixIndexException;
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array
+ * of double values.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ double [] getRowAsDoubleArray(int row) throws MatrixIndexException;
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param col the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ BigDecimal[] getColumn(int col) throws MatrixIndexException;
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array
+ * of double values.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param col the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ double [] getColumnAsDoubleArray(int col) throws MatrixIndexException;
+
+ /**
+ * Returns the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ */
+ BigDecimal getEntry(int row, int column) throws MatrixIndexException;
+
+ /**
+ * Returns the entry in the specified row and column as a double.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ */
+ double getEntryAsDouble(int row, int column) throws MatrixIndexException;
+
+ /**
+ * Returns the transpose of this matrix.
+ *
+ * @return transpose matrix
+ */
+ BigMatrix transpose();
+
+ /**
+ * Returns the inverse of this matrix.
+ *
+ * @return inverse matrix
+ * @throws org.apache.commons.math.linear.InvalidMatrixException if
+ * this is not invertible
+ */
+ BigMatrix inverse() throws InvalidMatrixException;
+
+ /**
+ * Returns the determinant of this matrix.
+ *
+ * @return determinant
+ *@throws org.apache.commons.math.linear.InvalidMatrixException if
+ * matrix is not square
+ */
+ BigDecimal getDeterminant() throws InvalidMatrixException;
+
+ /**
+ * 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
+ */
+ BigDecimal getTrace();
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ BigDecimal[] operate(BigDecimal[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ BigDecimal[] preMultiply(BigDecimal[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the solution vector for a linear system with coefficient
+ * matrix = this and constant vector = <code>b</code>.
+ *
+ * @param b constant vector
+ * @return vector of solution values to AX = b, where A is *this
+ * @throws IllegalArgumentException if this.rowDimension != b.length
+ * @throws org.apache.commons.math.linear.InvalidMatrixException if this matrix is not square or is singular
+ */
+ BigDecimal[] solve(BigDecimal[] b) throws IllegalArgumentException, InvalidMatrixException;
+
+ /**
+ * Returns a matrix of (column) solution vectors for linear systems with
+ * coefficient matrix = this and constant vectors = columns of
+ * <code>b</code>.
+ *
+ * @param b matrix of constant vectors forming RHS of linear systems to
+ * to solve
+ * @return matrix of solution vectors
+ * @throws IllegalArgumentException if this.rowDimension != row dimension
+ * @throws org.apache.commons.math.linear.InvalidMatrixException if this matrix is not square or is singular
+ */
+ BigMatrix solve(BigMatrix b) throws IllegalArgumentException, InvalidMatrixException;
+}
+
diff --git a/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java b/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java
new file mode 100644
index 0000000..80643a0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java
@@ -0,0 +1,1505 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Implementation of {@link BigMatrix} using a BigDecimal[][] array to store entries
+ * and <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
+ * LU decompostion</a> to support linear system
+ * solution and inverse.
+ * <p>
+ * The LU decompostion is performed as needed, to support the following operations: <ul>
+ * <li>solve</li>
+ * <li>isSingular</li>
+ * <li>getDeterminant</li>
+ * <li>inverse</li> </ul></p>
+ * <p>
+* <strong>Usage notes</strong>:<br>
+ * <ul><li>
+ * The LU decomposition is stored and reused on subsequent calls. If matrix
+ * data are modified using any of the public setXxx methods, the saved
+ * decomposition is discarded. If data are modified via references to the
+ * underlying array obtained using <code>getDataRef()</code>, then the stored
+ * LU decomposition will not be discarded. In this case, you need to
+ * explicitly invoke <code>LUDecompose()</code> to recompute the decomposition
+ * before using any of the methods above.</li>
+ * <li>
+ * As specified in the {@link BigMatrix} 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.</li></ul></p>
+ *
+ * @deprecated as of 2.0, replaced by {@link Array2DRowFieldMatrix} with a {@link
+ * org.apache.commons.math.util.BigReal} parameter
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+@Deprecated
+public class BigMatrixImpl implements BigMatrix, Serializable {
+
+ /** BigDecimal 0 */
+ static final BigDecimal ZERO = new BigDecimal(0);
+
+ /** BigDecimal 1 */
+ static final BigDecimal ONE = new BigDecimal(1);
+
+ /** Bound to determine effective singularity in LU decomposition */
+ private static final BigDecimal TOO_SMALL = new BigDecimal(10E-12);
+
+ /** Serialization id */
+ private static final long serialVersionUID = -1011428905656140431L;
+
+ /** Entries of the matrix */
+ protected BigDecimal data[][] = null;
+
+ /** Entries of cached LU decomposition.
+ * All updates to data (other than luDecompose()) *must* set this to null
+ */
+ protected BigDecimal lu[][] = null;
+
+ /** Permutation associated with LU decomposition */
+ protected int[] permutation = null;
+
+ /** Parity of the permutation associated with the LU decomposition */
+ protected int parity = 1;
+
+ /** Rounding mode for divisions **/
+ private int roundingMode = BigDecimal.ROUND_HALF_UP;
+
+ /*** BigDecimal scale ***/
+ private int scale = 64;
+
+ /**
+ * Creates a matrix with no data
+ */
+ public BigMatrixImpl() {
+ }
+
+ /**
+ * Create a new BigMatrix 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 IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public BigMatrixImpl(int rowDimension, int columnDimension) {
+ if (rowDimension < 1 ) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, rowDimension, 1);
+ }
+ if (columnDimension < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, columnDimension, 1);
+ }
+ data = new BigDecimal[rowDimension][columnDimension];
+ lu = null;
+ }
+
+ /**
+ * Create a new BigMatrix using <code>d</code> as the underlying
+ * data array.
+ * <p>The input array is copied, not referenced. This constructor has
+ * the same effect as calling {@link #BigMatrixImpl(BigDecimal[][], boolean)}
+ * with the second argument set to <code>true</code>.</p>
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ */
+ public BigMatrixImpl(BigDecimal[][] d) {
+ this.copyIn(d);
+ lu = null;
+ }
+
+ /**
+ * Create a new BigMatrix using the input array as the underlying
+ * data array.
+ * <p>If an array is built specially in order to be embedded in a
+ * BigMatrix and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param d data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #BigMatrixImpl(BigDecimal[][])
+ */
+ public BigMatrixImpl(BigDecimal[][] d, boolean copyArray) {
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ if (d == null) {
+ throw new NullPointerException();
+ }
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, d[r].length);
+ }
+ }
+ data = d;
+ }
+ lu = null;
+ }
+
+ /**
+ * Create a new BigMatrix using <code>d</code> as the underlying
+ * data array.
+ * <p>Since the underlying array will hold <code>BigDecimal</code>
+ * instances, it will be created.</p>
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ */
+ public BigMatrixImpl(double[][] d) {
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int row = 1; row < nRows; row++) {
+ if (d[row].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, d[row].length);
+ }
+ }
+ this.copyIn(d);
+ lu = null;
+ }
+
+ /**
+ * Create a new BigMatrix using the values represented by the strings in
+ * <code>d</code> as the underlying data array.
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ */
+ public BigMatrixImpl(String[][] d) {
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int row = 1; row < nRows; row++) {
+ if (d[row].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, d[row].length);
+ }
+ }
+ this.copyIn(d);
+ lu = null;
+ }
+
+ /**
+ * Create a new (column) BigMatrix using <code>v</code> as the
+ * data for the unique column of the <code>v.length x 1</code> matrix
+ * created.
+ * <p>
+ * The input array is copied, not referenced.</p>
+ *
+ * @param v column vector holding data for new matrix
+ */
+ public BigMatrixImpl(BigDecimal[] v) {
+ final int nRows = v.length;
+ data = new BigDecimal[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = v[row];
+ }
+ }
+
+ /**
+ * Create a new BigMatrix which is a copy of this.
+ *
+ * @return the cloned matrix
+ */
+ public BigMatrix copy() {
+ return new BigMatrixImpl(this.copyOut(), false);
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BigMatrix add(BigMatrix m) throws IllegalArgumentException {
+ try {
+ return add((BigMatrixImpl) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].add(m.getEntry(row, col));
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BigMatrixImpl add(BigMatrixImpl m) throws IllegalArgumentException {
+
+ // safety check
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] mRow = m.data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].add(mRow[col]);
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BigMatrix subtract(BigMatrix m) throws IllegalArgumentException {
+ try {
+ return subtract((BigMatrixImpl) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].subtract(getEntry(row, col));
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BigMatrixImpl subtract(BigMatrixImpl m) throws IllegalArgumentException {
+
+ // safety check
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] mRow = m.data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].subtract(mRow[col]);
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Returns the result of adding d to each entry of this.
+ *
+ * @param d value to be added to each entry
+ * @return d + this
+ */
+ public BigMatrix scalarAdd(BigDecimal d) {
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].add(d);
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Returns the result of multiplying each entry of this by <code>d</code>
+ * @param d value to multiply all entries by
+ * @return d * this
+ */
+ public BigMatrix scalarMultiply(BigDecimal d) {
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].multiply(d);
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Returns the result of postmultiplying this by <code>m</code>.
+ * @param m matrix to postmultiply by
+ * @return this*m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public BigMatrix multiply(BigMatrix m) throws IllegalArgumentException {
+ try {
+ return multiply((BigMatrixImpl) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int nRows = this.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = this.getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[nRows][nCols];
+ for (int row = 0; row < nRows; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < nCols; col++) {
+ BigDecimal sum = ZERO;
+ for (int i = 0; i < nSum; i++) {
+ sum = sum.add(dataRow[i].multiply(m.getEntry(i, col)));
+ }
+ outDataRow[col] = sum;
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by <code>m</code>.
+ * @param m matrix to postmultiply by
+ * @return this*m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public BigMatrixImpl multiply(BigMatrixImpl m) throws IllegalArgumentException {
+
+ // safety check
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int nRows = this.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = this.getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[nRows][nCols];
+ for (int row = 0; row < nRows; row++) {
+ final BigDecimal[] dataRow = data[row];
+ final BigDecimal[] outDataRow = outData[row];
+ for (int col = 0; col < nCols; col++) {
+ BigDecimal sum = ZERO;
+ for (int i = 0; i < nSum; i++) {
+ sum = sum.add(dataRow[i].multiply(m.data[i][col]));
+ }
+ outDataRow[col] = sum;
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Returns the result premultiplying this by <code>m</code>.
+ * @param m matrix to premultiply by
+ * @return m * this
+ * @throws IllegalArgumentException
+ * if rowDimension(this) != columnDimension(m)
+ */
+ public BigMatrix preMultiply(BigMatrix m) throws IllegalArgumentException {
+ return m.multiply(this);
+ }
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ * <p>
+ * Makes a fresh copy of the underlying data.</p>
+ *
+ * @return 2-dimensional array of entries
+ */
+ public BigDecimal[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ * <p>
+ * Makes a fresh copy of the underlying data converted to
+ * <code>double</code> values.</p>
+ *
+ * @return 2-dimensional array of entries
+ */
+ public double[][] getDataAsDoubleArray() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final double d[][] = new double[nRows][nCols];
+ for (int i = 0; i < nRows; i++) {
+ for (int j = 0; j < nCols; j++) {
+ d[i][j] = data[i][j].doubleValue();
+ }
+ }
+ return d;
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ * <p>
+ * Does not make a fresh copy of the underlying data.</p>
+ *
+ * @return 2-dimensional array of entries
+ */
+ public BigDecimal[][] getDataRef() {
+ return data;
+ }
+
+ /***
+ * Gets the rounding mode for division operations
+ * The default is {@link java.math.BigDecimal#ROUND_HALF_UP}
+ * @see BigDecimal
+ * @return the rounding mode.
+ */
+ public int getRoundingMode() {
+ return roundingMode;
+ }
+
+ /***
+ * Sets the rounding mode for decimal divisions.
+ * @see BigDecimal
+ * @param roundingMode rounding mode for decimal divisions
+ */
+ public void setRoundingMode(int roundingMode) {
+ this.roundingMode = roundingMode;
+ }
+
+ /***
+ * Sets the scale for division operations.
+ * The default is 64
+ * @see BigDecimal
+ * @return the scale
+ */
+ public int getScale() {
+ return scale;
+ }
+
+ /***
+ * Sets the scale for division operations.
+ * @see BigDecimal
+ * @param scale scale for division operations
+ */
+ public void setScale(int scale) {
+ this.scale = scale;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html">
+ * maximum absolute row sum norm</a> of the matrix.
+ *
+ * @return norm
+ */
+ public BigDecimal getNorm() {
+ BigDecimal maxColSum = ZERO;
+ for (int col = 0; col < this.getColumnDimension(); col++) {
+ BigDecimal sum = ZERO;
+ for (int row = 0; row < this.getRowDimension(); row++) {
+ sum = sum.add(data[row][col].abs());
+ }
+ maxColSum = maxColSum.max(sum);
+ }
+ return maxColSum;
+ }
+
+ /**
+ * Gets a submatrix. 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
+ * @return The subMatrix containing the data of the
+ * specified rows and columns
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ public BigMatrix getSubMatrix(int startRow, int endRow,
+ int startColumn, int endColumn)
+ throws MatrixIndexException {
+
+ MatrixUtils.checkRowIndex(this, startRow);
+ MatrixUtils.checkRowIndex(this, endRow);
+ if (startRow > endRow) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
+ startRow, endRow);
+ }
+
+ MatrixUtils.checkColumnIndex(this, startColumn);
+ MatrixUtils.checkColumnIndex(this, endColumn);
+ if (startColumn > endColumn) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
+ startColumn, endColumn);
+ }
+
+ final BigDecimal[][] subMatrixData =
+ new BigDecimal[endRow - startRow + 1][endColumn - startColumn + 1];
+ for (int i = startRow; i <= endRow; i++) {
+ System.arraycopy(data[i], startColumn,
+ subMatrixData[i - startRow], 0,
+ endColumn - startColumn + 1);
+ }
+
+ return new BigMatrixImpl(subMatrixData, false);
+
+ }
+
+ /**
+ * Gets a submatrix. Rows and columns are indicated
+ * counting from 0 to n-1.
+ *
+ * @param selectedRows Array of row indices must be non-empty
+ * @param selectedColumns Array of column indices must be non-empty
+ * @return The subMatrix containing the data in the
+ * specified rows and columns
+ * @exception MatrixIndexException if supplied row or column index arrays
+ * are not valid
+ */
+ public BigMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws MatrixIndexException {
+
+ if (selectedRows.length * selectedColumns.length == 0) {
+ if (selectedRows.length == 0) {
+ throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
+ }
+ throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY);
+ }
+
+ final BigDecimal[][] subMatrixData =
+ new BigDecimal[selectedRows.length][selectedColumns.length];
+ try {
+ for (int i = 0; i < selectedRows.length; i++) {
+ final BigDecimal[] subI = subMatrixData[i];
+ final BigDecimal[] dataSelectedI = data[selectedRows[i]];
+ for (int j = 0; j < selectedColumns.length; j++) {
+ subI[j] = dataSelectedI[selectedColumns[j]];
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // we redo the loop with checks enabled
+ // in order to generate an appropriate message
+ for (final int row : selectedRows) {
+ MatrixUtils.checkRowIndex(this, row);
+ }
+ for (final int column : selectedColumns) {
+ MatrixUtils.checkColumnIndex(this, column);
+ }
+ }
+ return new BigMatrixImpl(subMatrixData, false);
+ }
+
+ /**
+ * Replace the submatrix starting at <code>row, column</code> using data in
+ * the input <code>subMatrix</code> 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></p>
+ *
+ * @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 MatrixIndexException if subMatrix does not fit into this
+ * matrix from element in (row, column)
+ * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>subMatrix</code> is null
+ * @since 1.1
+ */
+ public void setSubMatrix(BigDecimal[][] subMatrix, int row, int column)
+ throws MatrixIndexException {
+
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+
+ for (int r = 1; r < nRows; r++) {
+ if (subMatrix[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, subMatrix[r].length);
+ }
+ }
+
+ if (data == null) {
+ if (row > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET,
+ row);
+ }
+ if (column > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET,
+ column);
+ }
+ data = new BigDecimal[nRows][nCols];
+ System.arraycopy(subMatrix, 0, data, 0, subMatrix.length);
+ } else {
+ 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++) {
+ System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols);
+ }
+
+ lu = null;
+
+ }
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row matrix
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ public BigMatrix getRowMatrix(int row) throws MatrixIndexException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int ncols = this.getColumnDimension();
+ final BigDecimal[][] out = new BigDecimal[1][ncols];
+ System.arraycopy(data[row], 0, out[0], 0, ncols);
+ return new BigMatrixImpl(out, false);
+ }
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a column matrix. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column matrix
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ public BigMatrix getColumnMatrix(int column) throws MatrixIndexException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = this.getRowDimension();
+ final BigDecimal[][] out = new BigDecimal[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ out[row][0] = data[row][column];
+ }
+ return new BigMatrixImpl(out, false);
+ }
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ public BigDecimal[] getRow(int row) throws MatrixIndexException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int ncols = this.getColumnDimension();
+ final BigDecimal[] out = new BigDecimal[ncols];
+ System.arraycopy(data[row], 0, out, 0, ncols);
+ return out;
+ }
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array
+ * of double values.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ public double[] getRowAsDoubleArray(int row) throws MatrixIndexException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int ncols = this.getColumnDimension();
+ final double[] out = new double[ncols];
+ for (int i=0;i<ncols;i++) {
+ out[i] = data[row][i].doubleValue();
+ }
+ return out;
+ }
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param col the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ public BigDecimal[] getColumn(int col) throws MatrixIndexException {
+ MatrixUtils.checkColumnIndex(this, col);
+ final int nRows = this.getRowDimension();
+ final BigDecimal[] out = new BigDecimal[nRows];
+ for (int i = 0; i < nRows; i++) {
+ out[i] = data[i][col];
+ }
+ return out;
+ }
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array
+ * of double values.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param col the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ public double[] getColumnAsDoubleArray(int col) throws MatrixIndexException {
+ MatrixUtils.checkColumnIndex(this, col);
+ final int nrows = this.getRowDimension();
+ final double[] out = new double[nrows];
+ for (int i=0;i<nrows;i++) {
+ out[i] = data[i][col].doubleValue();
+ }
+ return out;
+ }
+
+ /**
+ * Returns the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ */
+ public BigDecimal getEntry(int row, int column)
+ throws MatrixIndexException {
+ try {
+ return data[row][column];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /**
+ * Returns the entry in the specified row and column as a double.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row
+ * or column index is not valid
+ */
+ public double getEntryAsDouble(int row, int column) throws MatrixIndexException {
+ return getEntry(row,column).doubleValue();
+ }
+
+ /**
+ * Returns the transpose matrix.
+ *
+ * @return transpose matrix
+ */
+ public BigMatrix transpose() {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ final BigDecimal[][] outData = new BigDecimal[nCols][nRows];
+ for (int row = 0; row < nRows; row++) {
+ final BigDecimal[] dataRow = data[row];
+ for (int col = 0; col < nCols; col++) {
+ outData[col][row] = dataRow[col];
+ }
+ }
+ return new BigMatrixImpl(outData, false);
+ }
+
+ /**
+ * Returns the inverse matrix if this matrix is invertible.
+ *
+ * @return inverse matrix
+ * @throws InvalidMatrixException if this is not invertible
+ */
+ public BigMatrix inverse() throws InvalidMatrixException {
+ return solve(MatrixUtils.createBigIdentityMatrix(getRowDimension()));
+ }
+
+ /**
+ * Returns the determinant of this matrix.
+ *
+ * @return determinant
+ * @throws InvalidMatrixException if matrix is not square
+ */
+ public BigDecimal getDeterminant() throws InvalidMatrixException {
+ if (!isSquare()) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+ if (isSingular()) { // note: this has side effect of attempting LU decomp if lu == null
+ return ZERO;
+ } else {
+ BigDecimal det = (parity == 1) ? ONE : ONE.negate();
+ for (int i = 0; i < getRowDimension(); i++) {
+ det = det.multiply(lu[i][i]);
+ }
+ return det;
+ }
+ }
+
+ /**
+ * Is this a square matrix?
+ * @return true if the matrix is square (rowDimension = columnDimension)
+ */
+ public boolean isSquare() {
+ return getColumnDimension() == getRowDimension();
+ }
+
+ /**
+ * Is this a singular matrix?
+ * @return true if the matrix is singular
+ */
+ public boolean isSingular() {
+ if (lu == null) {
+ try {
+ luDecompose();
+ return false;
+ } catch (InvalidMatrixException ex) {
+ return true;
+ }
+ } else { // LU decomp must have been successfully performed
+ return false; // so the matrix is not singular
+ }
+ }
+
+ /**
+ * Returns the number of rows in the matrix.
+ *
+ * @return rowDimension
+ */
+ public int getRowDimension() {
+ return data.length;
+ }
+
+ /**
+ * Returns the number of columns in the matrix.
+ *
+ * @return columnDimension
+ */
+ public int getColumnDimension() {
+ return data[0].length;
+ }
+
+ /**
+ * 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 IllegalArgumentException if this matrix is not square.
+ */
+ public BigDecimal getTrace() throws IllegalArgumentException {
+ if (!isSquare()) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+ BigDecimal trace = data[0][0];
+ for (int i = 1; i < this.getRowDimension(); i++) {
+ trace = trace.add(data[i][i]);
+ }
+ return trace;
+ }
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ public BigDecimal[] operate(BigDecimal[] v) throws IllegalArgumentException {
+ if (v.length != getColumnDimension()) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, getColumnDimension() );
+ }
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ final BigDecimal[] out = new BigDecimal[nRows];
+ for (int row = 0; row < nRows; row++) {
+ BigDecimal sum = ZERO;
+ for (int i = 0; i < nCols; i++) {
+ sum = sum.add(data[row][i].multiply(v[i]));
+ }
+ out[row] = sum;
+ }
+ return out;
+ }
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ public BigDecimal[] operate(double[] v) throws IllegalArgumentException {
+ final BigDecimal bd[] = new BigDecimal[v.length];
+ for (int i = 0; i < bd.length; i++) {
+ bd[i] = new BigDecimal(v[i]);
+ }
+ return operate(bd);
+ }
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ public BigDecimal[] preMultiply(BigDecimal[] v) throws IllegalArgumentException {
+ final int nRows = this.getRowDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, nRows );
+ }
+ final int nCols = this.getColumnDimension();
+ final BigDecimal[] out = new BigDecimal[nCols];
+ for (int col = 0; col < nCols; col++) {
+ BigDecimal sum = ZERO;
+ for (int i = 0; i < nRows; i++) {
+ sum = sum.add(data[i][col].multiply(v[i]));
+ }
+ out[col] = sum;
+ }
+ return out;
+ }
+
+ /**
+ * Returns a matrix of (column) solution vectors for linear systems with
+ * coefficient matrix = this and constant vectors = columns of
+ * <code>b</code>.
+ *
+ * @param b array of constants forming RHS of linear systems to
+ * to solve
+ * @return solution array
+ * @throws IllegalArgumentException if this.rowDimension != row dimension
+ * @throws InvalidMatrixException if this matrix is not square or is singular
+ */
+ public BigDecimal[] solve(BigDecimal[] b) throws IllegalArgumentException, InvalidMatrixException {
+ final int nRows = this.getRowDimension();
+ if (b.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.length, nRows);
+ }
+ final BigMatrix bMatrix = new BigMatrixImpl(b);
+ final BigDecimal[][] solution = ((BigMatrixImpl) (solve(bMatrix))).getDataRef();
+ final BigDecimal[] out = new BigDecimal[nRows];
+ for (int row = 0; row < nRows; row++) {
+ out[row] = solution[row][0];
+ }
+ return out;
+ }
+
+ /**
+ * Returns a matrix of (column) solution vectors for linear systems with
+ * coefficient matrix = this and constant vectors = columns of
+ * <code>b</code>.
+ *
+ * @param b array of constants forming RHS of linear systems to
+ * to solve
+ * @return solution array
+ * @throws IllegalArgumentException if this.rowDimension != row dimension
+ * @throws InvalidMatrixException if this matrix is not square or is singular
+ */
+ public BigDecimal[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException {
+ final BigDecimal bd[] = new BigDecimal[b.length];
+ for (int i = 0; i < bd.length; i++) {
+ bd[i] = new BigDecimal(b[i]);
+ }
+ return solve(bd);
+ }
+
+ /**
+ * Returns a matrix of (column) solution vectors for linear systems with
+ * coefficient matrix = this and constant vectors = columns of
+ * <code>b</code>.
+ *
+ * @param b matrix of constant vectors forming RHS of linear systems to
+ * to solve
+ * @return matrix of solution vectors
+ * @throws IllegalArgumentException if this.rowDimension != row dimension
+ * @throws InvalidMatrixException if this matrix is not square or is singular
+ */
+ public BigMatrix solve(BigMatrix b) throws IllegalArgumentException, InvalidMatrixException {
+ if (b.getRowDimension() != getRowDimension()) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), getRowDimension(), "n");
+ }
+ if (!isSquare()) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+ if (this.isSingular()) { // side effect: compute LU decomp
+ throw new SingularMatrixException();
+ }
+
+ final int nCol = this.getColumnDimension();
+ final int nColB = b.getColumnDimension();
+ final int nRowB = b.getRowDimension();
+
+ // Apply permutations to b
+ final BigDecimal[][] bp = new BigDecimal[nRowB][nColB];
+ for (int row = 0; row < nRowB; row++) {
+ final BigDecimal[] bpRow = bp[row];
+ for (int col = 0; col < nColB; col++) {
+ bpRow[col] = b.getEntry(permutation[row], col);
+ }
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < nCol; col++) {
+ for (int i = col + 1; i < nCol; i++) {
+ final BigDecimal[] bpI = bp[i];
+ final BigDecimal[] luI = lu[i];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col]));
+ }
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = nCol - 1; col >= 0; col--) {
+ final BigDecimal[] bpCol = bp[col];
+ final BigDecimal luDiag = lu[col][col];
+ for (int j = 0; j < nColB; j++) {
+ bpCol[j] = bpCol[j].divide(luDiag, scale, roundingMode);
+ }
+ for (int i = 0; i < col; i++) {
+ final BigDecimal[] bpI = bp[i];
+ final BigDecimal[] luI = lu[i];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col]));
+ }
+ }
+ }
+
+ return new BigMatrixImpl(bp, false);
+
+ }
+
+ /**
+ * Computes a new
+ * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
+ * LU decompostion</a> for this matrix, storing the result for use by other methods.
+ * <p>
+ * <strong>Implementation Note</strong>:<br>
+ * Uses <a href="http://www.damtp.cam.ac.uk/user/fdl/people/sd/lectures/nummeth98/linear.htm">
+ * Crout's algortithm</a>, with partial pivoting.</p>
+ * <p>
+ * <strong>Usage Note</strong>:<br>
+ * This method should rarely be invoked directly. Its only use is
+ * to force recomputation of the LU decomposition when changes have been
+ * made to the underlying data using direct array references. Changes
+ * made using setXxx methods will trigger recomputation when needed
+ * automatically.</p>
+ *
+ * @throws InvalidMatrixException if the matrix is non-square or singular.
+ */
+ public void luDecompose() throws InvalidMatrixException {
+
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (nRows != nCols) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+ lu = this.getData();
+
+ // Initialize permutation array and parity
+ permutation = new int[nRows];
+ for (int row = 0; row < nRows; row++) {
+ permutation[row] = row;
+ }
+ parity = 1;
+
+ // Loop over columns
+ for (int col = 0; col < nCols; col++) {
+
+ BigDecimal sum = ZERO;
+
+ // upper
+ for (int row = 0; row < col; row++) {
+ final BigDecimal[] 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 max = col; // permutation row
+ BigDecimal largest = ZERO;
+ for (int row = col; row < nRows; row++) {
+ final BigDecimal[] 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;
+
+ // maintain best permutation choice
+ if (sum.abs().compareTo(largest) == 1) {
+ largest = sum.abs();
+ max = row;
+ }
+ }
+
+ // Singularity check
+ if (lu[max][col].abs().compareTo(TOO_SMALL) <= 0) {
+ lu = null;
+ throw new SingularMatrixException();
+ }
+
+ // Pivot if necessary
+ if (max != col) {
+ BigDecimal tmp = ZERO;
+ for (int i = 0; i < nCols; i++) {
+ tmp = lu[max][i];
+ lu[max][i] = lu[col][i];
+ lu[col][i] = tmp;
+ }
+ int temp = permutation[max];
+ permutation[max] = permutation[col];
+ permutation[col] = temp;
+ parity = -parity;
+ }
+
+ // Divide the lower elements by the "winning" diagonal elt.
+ final BigDecimal luDiag = lu[col][col];
+ for (int row = col + 1; row < nRows; row++) {
+ final BigDecimal[] luRow = lu[row];
+ luRow[col] = luRow[col].divide(luDiag, scale, roundingMode);
+ }
+
+ }
+
+ }
+
+ /**
+ * Get a string representation for this matrix.
+ * @return a string representation for this matrix
+ */
+ @Override
+ public String toString() {
+ StringBuilder res = new StringBuilder();
+ res.append("BigMatrixImpl{");
+ if (data != null) {
+ for (int i = 0; i < data.length; i++) {
+ if (i > 0) {
+ res.append(",");
+ }
+ res.append("{");
+ for (int j = 0; j < data[0].length; j++) {
+ if (j > 0) {
+ res.append(",");
+ }
+ res.append(data[i][j]);
+ }
+ res.append("}");
+ }
+ }
+ res.append("}");
+ return res.toString();
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a
+ * <code>BigMatrixImpl</code> instance with the same dimensions as this
+ * and all corresponding matrix entries are equal. BigDecimal.equals
+ * is used to compare corresponding entries.
+ *
+ * @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 BigMatrixImpl == false) {
+ return false;
+ }
+ final BigMatrix m = (BigMatrix) 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++) {
+ final BigDecimal[] dataRow = data[row];
+ for (int col = 0; col < nCols; col++) {
+ if (!dataRow[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 = 7;
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ ret = ret * 31 + nRows;
+ ret = ret * 31 + nCols;
+ for (int row = 0; row < nRows; row++) {
+ final BigDecimal[] dataRow = data[row];
+ for (int col = 0; col < nCols; col++) {
+ ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) *
+ dataRow[col].hashCode();
+ }
+ }
+ return ret;
+ }
+
+ //------------------------ Protected methods
+
+ /**
+ * Returns the LU decomposition as a BigMatrix.
+ * Returns a fresh copy of the cached LU matrix if this has been computed;
+ * otherwise the composition is computed and cached for use by other methods.
+ * Since a copy is returned in either case, changes to the returned matrix do not
+ * affect the LU decomposition property.
+ * <p>
+ * The matrix returned is a compact representation of the LU decomposition.
+ * Elements below the main diagonal correspond to entries of the "L" matrix;
+ * elements on and above the main diagonal correspond to entries of the "U"
+ * matrix.</p>
+ * <p>
+ * Example: <pre>
+ *
+ * Returned matrix L U
+ * 2 3 1 1 0 0 2 3 1
+ * 5 4 6 5 1 0 0 4 6
+ * 1 7 8 1 7 1 0 0 8
+ * </pre>
+ *
+ * The L and U matrices satisfy the matrix equation LU = permuteRows(this), <br>
+ * where permuteRows reorders the rows of the matrix to follow the order determined
+ * by the <a href=#getPermutation()>permutation</a> property.</p>
+ *
+ * @return LU decomposition matrix
+ * @throws InvalidMatrixException if the matrix is non-square or singular.
+ */
+ protected BigMatrix getLUMatrix() throws InvalidMatrixException {
+ if (lu == null) {
+ luDecompose();
+ }
+ return new BigMatrixImpl(lu);
+ }
+
+ /**
+ * Returns the permutation associated with the lu decomposition.
+ * The entries of the array represent a permutation of the numbers 0, ... , nRows - 1.
+ * <p>
+ * Example:
+ * permutation = [1, 2, 0] means current 2nd row is first, current third row is second
+ * and current first row is last.</p>
+ * <p>
+ * Returns a fresh copy of the array.</p>
+ *
+ * @return the permutation
+ */
+ protected int[] getPermutation() {
+ final int[] out = new int[permutation.length];
+ System.arraycopy(permutation, 0, out, 0, permutation.length);
+ return out;
+ }
+
+ //------------------------ Private methods
+
+ /**
+ * Returns a fresh copy of the underlying data array.
+ *
+ * @return a copy of the underlying data array.
+ */
+ private BigDecimal[][] copyOut() {
+ final int nRows = this.getRowDimension();
+ final BigDecimal[][] out = new BigDecimal[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;
+ }
+
+ /**
+ * Replaces data with a fresh copy of the input array.
+ * <p>
+ * Verifies that the input array is rectangular and non-empty.</p>
+ *
+ * @param in data to copy in
+ * @throws IllegalArgumentException if input array is emtpy or not
+ * rectangular
+ * @throws NullPointerException if input array is null
+ */
+ private void copyIn(BigDecimal[][] in) {
+ setSubMatrix(in,0,0);
+ }
+
+ /**
+ * Replaces data with a fresh copy of the input array.
+ *
+ * @param in data to copy in
+ */
+ private void copyIn(double[][] in) {
+ final int nRows = in.length;
+ final int nCols = in[0].length;
+ data = new BigDecimal[nRows][nCols];
+ for (int i = 0; i < nRows; i++) {
+ final BigDecimal[] dataI = data[i];
+ final double[] inI = in[i];
+ for (int j = 0; j < nCols; j++) {
+ dataI[j] = new BigDecimal(inI[j]);
+ }
+ }
+ lu = null;
+ }
+
+ /**
+ * Replaces data with BigDecimals represented by the strings in the input
+ * array.
+ *
+ * @param in data to copy in
+ */
+ private void copyIn(String[][] in) {
+ final int nRows = in.length;
+ final int nCols = in[0].length;
+ data = new BigDecimal[nRows][nCols];
+ for (int i = 0; i < nRows; i++) {
+ final BigDecimal[] dataI = data[i];
+ final String[] inI = in[i];
+ for (int j = 0; j < nCols; j++) {
+ dataI[j] = new BigDecimal(inI[j]);
+ }
+ }
+ lu = null;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/BlockFieldMatrix.java b/src/main/java/org/apache/commons/math/linear/BlockFieldMatrix.java
new file mode 100644
index 0000000..35ae1c2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/BlockFieldMatrix.java
@@ -0,0 +1,1670 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ * <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>
+ * <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>
+ * <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>
+ * <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.
+ * </p>
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 the number of rows in the new matrix
+ * @param columns the number of columns in the new matrix
+ * @throws IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public BlockFieldMatrix(final Field<T> field, final int rows, final int columns)
+ throws IllegalArgumentException {
+
+ 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>
+ * <p>Calling this constructor is equivalent to call:
+ * <pre>matrix = new BlockFieldMatrix<T>(getField(), rawData.length, rawData[0].length,
+ * toBlocksLayout(rawData), false);</pre>
+ * </p>
+ * @param rawData data for new matrix, in raw layout
+ *
+ * @exception IllegalArgumentException if <code>blockData</code> shape is
+ * inconsistent with block layout
+ * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
+ */
+ public BlockFieldMatrix(final T[][] rawData)
+ throws IllegalArgumentException {
+ 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.</p>
+ * @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
+ *
+ * @exception IllegalArgumentException if <code>blockData</code> shape is
+ * inconsistent with block layout
+ * @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 IllegalArgumentException {
+
+ 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 = 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.WRONG_BLOCK_LENGTH,
+ 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>
+ * <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.
+ * </p>
+ * @param <T> the 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
+ * @exception IllegalArgumentException if <code>rawData</code> 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 IllegalArgumentException {
+
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ columns, length);
+ }
+ }
+
+ // convert array
+ final Field<T> field = extractField(rawData);
+ final T[][] blocks = 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 = 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.
+ * </p>
+ * @param <T> the type of the field elements
+ * @param field field to which the elements belong
+ * @param rows the number of rows in the new matrix
+ * @param columns the 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 = 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] = buildArray(field, iHeight * jWidth);
+ ++blockIndex;
+ }
+ }
+
+ return blocks;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ 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 IllegalArgumentException {
+ 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 this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BlockFieldMatrix<T> add(final BlockFieldMatrix<T> m)
+ throws IllegalArgumentException {
+
+ // 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 IllegalArgumentException {
+ 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 this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this - m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BlockFieldMatrix<T> subtract(final BlockFieldMatrix<T> m)
+ throws IllegalArgumentException {
+
+ // 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)
+ throws IllegalArgumentException {
+
+ 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)
+ throws IllegalArgumentException {
+
+ 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 IllegalArgumentException {
+ 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 this by m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public BlockFieldMatrix<T> multiply(BlockFieldMatrix<T> m) throws IllegalArgumentException {
+
+ // 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 = 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 MatrixIndexException {
+
+ // 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.</p>
+ * @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 MatrixIndexException {
+
+ // safety checks
+ final int refLength = subMatrix[0].length;
+ if (refLength < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ 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 MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance row
+ */
+ public void setRowMatrix(final int row, final BlockFieldMatrix<T> matrix)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) ||
+ (matrix.getColumnDimension() != nCols)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ try {
+ setColumnMatrix(column, (BlockFieldMatrix<T>) 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 MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance column
+ */
+ void setColumnMatrix(final int column, final BlockFieldMatrix<T> matrix)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) ||
+ (matrix.getColumnDimension() != 1)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ checkRowIndex(row);
+ final T[] outData = 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>(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowVector(final int row, final FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException {
+ try {
+ setRow(row, ((ArrayFieldVector<T>) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setRowVector(row, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector<T> getColumnVector(final int column)
+ throws MatrixIndexException {
+
+ checkColumnIndex(column);
+ final T[] outData = 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>(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumnVector(final int column, final FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException {
+ try {
+ setColumn(column, ((ArrayFieldVector<T>) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setColumnVector(column, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] getRow(final int row)
+ throws MatrixIndexException {
+
+ checkRowIndex(row);
+ final T[] out = 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 MatrixIndexException, InvalidMatrixException {
+
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ checkColumnIndex(column);
+ final T[] out = 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 MatrixIndexException, InvalidMatrixException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ try {
+ 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];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final T value)
+ throws MatrixIndexException {
+ try {
+ 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;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final T increment)
+ throws MatrixIndexException {
+ try {
+ 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);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final T factor)
+ throws MatrixIndexException {
+ try {
+ 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);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@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 IllegalArgumentException {
+
+ if (v.length != columns) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, columns);
+ }
+ final T[] out = 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 IllegalArgumentException {
+
+ if (v.length != rows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ v.length, rows);
+ }
+ final T[] out = 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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/math/linear/BlockRealMatrix.java b/src/main/java/org/apache/commons/math/linear/BlockRealMatrix.java
new file mode 100644
index 0000000..5e7060e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/BlockRealMatrix.java
@@ -0,0 +1,1690 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ * <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>
+ * <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>
+ * <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>
+ * <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.
+ * </p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public BlockRealMatrix(final int rows, final int columns)
+ throws IllegalArgumentException {
+
+ 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>
+ * <p>Calling this constructor is equivalent to call:
+ * <pre>matrix = new BlockRealMatrix(rawData.length, rawData[0].length,
+ * toBlocksLayout(rawData), false);</pre>
+ * </p>
+ * @param rawData data for new matrix, in raw layout
+ *
+ * @exception IllegalArgumentException if <code>blockData</code> shape is
+ * inconsistent with block layout
+ * @see #BlockRealMatrix(int, int, double[][], boolean)
+ */
+ public BlockRealMatrix(final double[][] rawData)
+ throws IllegalArgumentException {
+ 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.</p>
+ * @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
+ *
+ * @exception IllegalArgumentException if <code>blockData</code> shape is
+ * inconsistent with block layout
+ * @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 IllegalArgumentException {
+
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.WRONG_BLOCK_LENGTH,
+ 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>
+ * <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.
+ * </p>
+ * @param rawData data array in raw layout
+ * @return a new data array containing the same entries but in blocks layout
+ * @exception IllegalArgumentException if <code>rawData</code> is not rectangular
+ * (not all rows have the same length)
+ * @see #createBlocksLayout(int, int)
+ * @see #BlockRealMatrix(int, int, double[][], boolean)
+ */
+ public static double[][] toBlocksLayout(final double[][] rawData)
+ throws IllegalArgumentException {
+
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ 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.
+ * </p>
+ * @param rows the number of rows in the new matrix
+ * @param columns the 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 IllegalArgumentException {
+ 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 IllegalArgumentException {
+ 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 and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BlockRealMatrix add(final BlockRealMatrix m)
+ throws IllegalArgumentException {
+
+ // 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 IllegalArgumentException {
+ 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;
+
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this - m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public BlockRealMatrix subtract(final BlockRealMatrix m)
+ throws IllegalArgumentException {
+
+ // 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)
+ throws IllegalArgumentException {
+
+ 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)
+ throws IllegalArgumentException {
+
+ 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 IllegalArgumentException {
+ 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 m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public BlockRealMatrix multiply(BlockRealMatrix m) throws IllegalArgumentException {
+
+ // 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 MatrixIndexException {
+
+ // 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.</p>
+ * @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 MatrixIndexException {
+
+ // safety checks
+ final int refLength = subMatrix[0].length;
+ if (refLength < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ 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 MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance row
+ */
+ public void setRowMatrix(final int row, final BlockRealMatrix matrix)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) ||
+ (matrix.getColumnDimension() != nCols)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ 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 MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance column
+ */
+ void setColumnMatrix(final int column, final BlockRealMatrix matrix)
+ throws MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) ||
+ (matrix.getColumnDimension() != 1)) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ try {
+ setRow(row, ((ArrayRealVector) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setRowVector(row, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getColumnVector(final int column)
+ throws MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+ try {
+ setColumn(column, ((ArrayRealVector) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setColumnVector(column, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getRow(final int row)
+ throws MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+
+ 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 MatrixIndexException, InvalidMatrixException {
+
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new InvalidMatrixException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ 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 MatrixIndexException {
+ try {
+ 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];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws MatrixIndexException {
+ try {
+ 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;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws MatrixIndexException {
+ try {
+ 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;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws MatrixIndexException {
+ try {
+ 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;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@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 IllegalArgumentException {
+
+ if (v.length != columns) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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 IllegalArgumentException {
+
+ if (v.length != rows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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/math/linear/CholeskyDecomposition.java b/src/main/java/org/apache/commons/math/linear/CholeskyDecomposition.java
new file mode 100644
index 0000000..d28523e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/CholeskyDecomposition.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.math.linear;
+
+
+/**
+ * An interface to classes that implement an algorithm to calculate the
+ * Cholesky decomposition of a real symmetric positive-definite matrix.
+ * <p>This interface 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:</p>
+ * <ul>
+ * <li>a {@link #getLT() getLT} method has been added,</li>
+ * <li>the <code>isspd</code> method has been removed, the constructors of
+ * implementation classes being expected to throw {@link
+ * NotPositiveDefiniteMatrixException} when a matrix cannot be decomposed,</li>
+ * <li>a {@link #getDeterminant() getDeterminant} method has been added,</li>
+ * <li>the <code>solve</code> method has been replaced by a {@link
+ * #getSolver() getSolver} method and the equivalent method provided by
+ * the returned {@link DecompositionSolver}.</li>
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/CholeskyDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Cholesky_decomposition">Wikipedia</a>
+ * @version $Revision: 826627 $ $Date: 2009-10-19 12:27:47 +0200 (lun. 19 oct. 2009) $
+ * @since 2.0
+ */
+public interface CholeskyDecomposition {
+
+ /**
+ * Returns the matrix L of the decomposition.
+ * <p>L is an lower-triangular matrix</p>
+ * @return the L matrix
+ */
+ RealMatrix getL();
+
+ /**
+ * Returns the transpose of the matrix L of the decomposition.
+ * <p>L<sup>T</sup> is an upper-triangular matrix</p>
+ * @return the transpose of the matrix L of the decomposition
+ */
+ RealMatrix getLT();
+
+ /**
+ * Return the determinant of the matrix
+ * @return determinant of the matrix
+ */
+ double getDeterminant();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ * @return a solver
+ */
+ DecompositionSolver getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/CholeskyDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/CholeskyDecompositionImpl.java
new file mode 100644
index 0000000..bde380e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/CholeskyDecompositionImpl.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.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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 that
+ * satisfy: A = LL<sup>T</sup>Q = I). In a sense, this is the square root of A.</p>
+ *
+ * @see <a href="http://mathworld.wolfram.com/CholeskyDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Cholesky_decomposition">Wikipedia</a>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class CholeskyDecompositionImpl implements 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
+ * #CholeskyDecompositionImpl(RealMatrix, double, double)} with the
+ * thresholds set to the default values {@link
+ * #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD} and {@link
+ * #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD}
+ * </p>
+ * @param matrix the matrix to decompose
+ * @exception NonSquareMatrixException if matrix is not square
+ * @exception NotSymmetricMatrixException if matrix is not symmetric
+ * @exception NotPositiveDefiniteMatrixException if the matrix is not
+ * strictly positive definite
+ * @see #CholeskyDecompositionImpl(RealMatrix, double, double)
+ * @see #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD
+ * @see #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD
+ */
+ public CholeskyDecompositionImpl(final RealMatrix matrix)
+ throws NonSquareMatrixException,
+ NotSymmetricMatrixException, NotPositiveDefiniteMatrixException {
+ 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
+ * @exception NonSquareMatrixException if matrix is not square
+ * @exception NotSymmetricMatrixException if matrix is not symmetric
+ * @exception NotPositiveDefiniteMatrixException if the matrix is not
+ * strictly positive definite
+ * @see #CholeskyDecompositionImpl(RealMatrix)
+ * @see #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD
+ * @see #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD
+ */
+ public CholeskyDecompositionImpl(final RealMatrix matrix,
+ final double relativeSymmetryThreshold,
+ final double absolutePositivityThreshold)
+ throws NonSquareMatrixException,
+ NotSymmetricMatrixException, NotPositiveDefiniteMatrixException {
+
+ 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 NotSymmetricMatrixException();
+ }
+ 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 NotPositiveDefiniteMatrixException();
+ }
+
+ 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];
+ }
+ }
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getL() {
+ if (cachedL == null) {
+ cachedL = getLT().transpose();
+ }
+ return cachedL;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getLT() {
+
+ if (cachedLT == null) {
+ cachedLT = MatrixUtils.createRealMatrix(lTData);
+ }
+
+ // return the cached matrix
+ return cachedLT;
+
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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 double[] solve(double[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = lTData.length;
+ if (b.length != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.length, m);
+ }
+
+ final double[] x = b.clone();
+
+ // 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 x;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ try {
+ return solve((ArrayRealVector) b);
+ } catch (ClassCastException cce) {
+
+ final int m = lTData.length;
+ if (b.getDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.getDimension(), m);
+ }
+
+ final double[] x = b.getData();
+
+ // 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);
+
+ }
+ }
+
+ /** Solve the linear equation A &times; X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X such that A &times; X = B
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ public ArrayRealVector solve(ArrayRealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ return new ArrayRealVector(solve(b.getDataRef()), false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = lTData.length;
+ if (b.getRowDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), m, "n");
+ }
+
+ final int nColB = b.getColumnDimension();
+ 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, false);
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getInverse() throws InvalidMatrixException {
+ return solve(MatrixUtils.createRealIdentityMatrix(lTData.length));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/DecompositionSolver.java b/src/main/java/org/apache/commons/math/linear/DecompositionSolver.java
new file mode 100644
index 0000000..4f7cb53
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/DecompositionSolver.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.math.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>
+ * <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.</p>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ double[] solve(final double[] b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /** 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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ RealVector solve(final RealVector b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /** 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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ RealMatrix solve(final RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /**
+ * 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 InvalidMatrixException if decomposed matrix is singular
+ */
+ RealMatrix getInverse()
+ throws InvalidMatrixException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixChangingVisitor.java
new file mode 100644
index 0000000..808b00d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixChangingVisitor.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.math.linear;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * 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.
+ * </p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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) throws MatrixVisitorException {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public T end() {
+ return zero;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixPreservingVisitor.java
new file mode 100644
index 0000000..e1fd606
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/DefaultFieldMatrixPreservingVisitor.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.math.linear;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * 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.
+ * </p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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)
+ throws MatrixVisitorException {
+ }
+
+ /** {@inheritDoc} */
+ public T end() {
+ return zero;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixChangingVisitor.java
new file mode 100644
index 0000000..c609d81
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixChangingVisitor.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.math.linear;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * 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.
+ * </p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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) throws MatrixVisitorException {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double end() {
+ return 0;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixPreservingVisitor.java
new file mode 100644
index 0000000..1ab5b14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/DefaultRealMatrixPreservingVisitor.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.math.linear;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * 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.
+ * </p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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)
+ throws MatrixVisitorException {
+ }
+
+ /** {@inheritDoc} */
+ public double end() {
+ return 0;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/EigenDecomposition.java b/src/main/java/org/apache/commons/math/linear/EigenDecomposition.java
new file mode 100644
index 0000000..f991e8b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/EigenDecomposition.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.math.linear;
+
+
+/**
+ * An interface to classes that implement an algorithm to calculate 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>
+ * <p>This interface 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:</p>
+ * <ul>
+ * <li>a {@link #getVT() getVt} method has been added,</li>
+ * <li>two {@link #getRealEigenvalue(int) getRealEigenvalue} and {@link #getImagEigenvalue(int)
+ * getImagEigenvalue} methods to pick up a single eigenvalue have been added,</li>
+ * <li>a {@link #getEigenvector(int) getEigenvector} method to pick up a single
+ * eigenvector has been added,</li>
+ * <li>a {@link #getDeterminant() getDeterminant} method has been added.</li>
+ * <li>a {@link #getSolver() getSolver} method has been added.</li>
+ * </ul>
+ * @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>
+ * @version $Revision: 997726 $ $Date: 2010-09-16 14:39:51 +0200 (jeu. 16 sept. 2010) $
+ * @since 2.0
+ */
+public interface EigenDecomposition {
+
+ /**
+ * Returns the matrix V of the decomposition.
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * <p>The columns of V are the eigenvectors of the original matrix.</p>
+ * <p>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).</p>
+ * @return the V matrix
+ */
+ RealMatrix getV();
+
+ /**
+ * Returns the block diagonal matrix D of the decomposition.
+ * <p>D is a block diagonal matrix.</p>
+ * <p>Real eigenvalues are on the diagonal while complex values are on
+ * 2x2 blocks { {real +imaginary}, {-imaginary, real} }.</p>
+ * @return the D matrix
+ * @see #getRealEigenvalues()
+ * @see #getImagEigenvalues()
+ */
+ RealMatrix getD();
+
+ /**
+ * Returns the transpose of the matrix V of the decomposition.
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * <p>The columns of V are the eigenvectors of the original matrix.</p>
+ * <p>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).</p>
+ * @return the transpose of the V matrix
+ */
+ RealMatrix getVT();
+
+ /**
+ * Returns 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()
+ */
+ double[] getRealEigenvalues();
+
+ /**
+ * 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)
+ */
+ double getRealEigenvalue(int i);
+
+ /**
+ * Returns 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()
+ */
+ double[] getImagEigenvalues();
+
+ /**
+ * Returns the imaginary part of the i<sup>th</sup> eigenvalue of the original matrix.
+ * @param i index of the eigenvalue (counting from 0)
+ * @return imaginary part of the i<sup>th</sup> eigenvalue of the original matrix
+ * @see #getD()
+ * @see #getImagEigenvalues()
+ * @see #getRealEigenvalue(int)
+ */
+ double getImagEigenvalue(int i);
+
+ /**
+ * Returns a copy of the i<sup>th</sup> eigenvector of the original matrix.
+ * @param i index of the eigenvector (counting from 0)
+ * @return copy of the i<sup>th</sup> eigenvector of the original matrix
+ * @see #getD()
+ */
+ RealVector getEigenvector(int i);
+
+ /**
+ * Return the determinant of the matrix
+ * @return determinant of the matrix
+ */
+ double getDeterminant();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in exact linear sense.
+ * @return a solver
+ */
+ DecompositionSolver getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/EigenDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/EigenDecompositionImpl.java
new file mode 100644
index 0000000..1b3085c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/EigenDecompositionImpl.java
@@ -0,0 +1,619 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Calculates the eigen decomposition of a real <strong>symmetric</strong>
+ * matrix.
+ * <p>
+ * The eigen decomposition of matrix A is a set of two matrices: V and D such
+ * that A = V D V<sup>T</sup>. A, V and D are all m &times; m matrices.
+ * </p>
+ * <p>
+ * As of 2.0, this class supports only <strong>symmetric</strong> matrices, and
+ * hence computes only real realEigenvalues. This implies the D matrix returned
+ * by {@link #getD()} is always diagonal and the imaginary values returned
+ * {@link #getImagEigenvalue(int)} and {@link #getImagEigenvalues()} are always
+ * null.
+ * </p>
+ * <p>
+ * When called with a {@link RealMatrix} argument, this implementation only uses
+ * the upper part of the matrix, the part below the diagonal is not accessed at
+ * all.
+ * </p>
+ * <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
+ * </p>
+ * @version $Revision: 1002040 $ $Date: 2010-09-28 09:18:31 +0200 (mar. 28 sept. 2010) $
+ * @since 2.0
+ */
+public class EigenDecompositionImpl implements EigenDecomposition {
+
+ /** 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;
+
+ /**
+ * Calculates the eigen decomposition of the given symmetric matrix.
+ * @param matrix The <strong>symmetric</strong> matrix to decompose.
+ * @param splitTolerance dummy parameter, present for backward compatibility only.
+ * @exception InvalidMatrixException (wrapping a
+ * {@link org.apache.commons.math.ConvergenceException} if algorithm
+ * fails to converge
+ */
+ public EigenDecompositionImpl(final RealMatrix matrix,final double splitTolerance)
+ throws InvalidMatrixException {
+ if (isSymmetric(matrix)) {
+ transformToTridiagonal(matrix);
+ findEigenVectors(transformer.getQ().getData());
+ } else {
+ // as of 2.0, non-symmetric matrices (i.e. complex eigenvalues) are
+ // NOT supported
+ // see issue https://issues.apache.org/jira/browse/MATH-235
+ throw new InvalidMatrixException(
+ LocalizedFormats.ASSYMETRIC_EIGEN_NOT_SUPPORTED);
+ }
+ }
+
+ /**
+ * 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 triadiagonal form
+ * @param secondary Secondary of the tridiagonal form
+ * @param splitTolerance dummy parameter, present for backward compatibility only.
+ * @exception InvalidMatrixException (wrapping a
+ * {@link org.apache.commons.math.ConvergenceException} if algorithm
+ * fails to converge
+ */
+ public EigenDecompositionImpl(final double[] main,final double[] secondary,
+ final double splitTolerance)
+ throws InvalidMatrixException {
+ this.main = main.clone();
+ this.secondary = secondary.clone();
+ transformer = null;
+ final int size=main.length;
+ double[][] z = new double[size][size];
+ for (int i=0;i<size;i++) {
+ z[i][i]=1.0;
+ }
+ findEigenVectors(z);
+ }
+
+ /**
+ * Check if a matrix is symmetric.
+ * @param matrix
+ * matrix to check
+ * @return true if matrix is symmetric
+ */
+ private boolean isSymmetric(final RealMatrix matrix) {
+ final int rows = matrix.getRowDimension();
+ final int columns = matrix.getColumnDimension();
+ final double eps = 10 * rows * columns * MathUtils.EPSILON;
+ for (int i = 0; i < rows; ++i) {
+ for (int j = i + 1; j < columns; ++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)) * eps)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getV() throws InvalidMatrixException {
+
+ 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;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getD() throws InvalidMatrixException {
+ if (cachedD == null) {
+ // cache the matrix for subsequent calls
+ cachedD = MatrixUtils.createRealDiagonalMatrix(realEigenvalues);
+ }
+ return cachedD;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getVT() throws InvalidMatrixException {
+
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getRealEigenvalues() throws InvalidMatrixException {
+ return realEigenvalues.clone();
+ }
+
+ /** {@inheritDoc} */
+ public double getRealEigenvalue(final int i) throws InvalidMatrixException,
+ ArrayIndexOutOfBoundsException {
+ return realEigenvalues[i];
+ }
+
+ /** {@inheritDoc} */
+ public double[] getImagEigenvalues() throws InvalidMatrixException {
+ return imagEigenvalues.clone();
+ }
+
+ /** {@inheritDoc} */
+ public double getImagEigenvalue(final int i) throws InvalidMatrixException,
+ ArrayIndexOutOfBoundsException {
+ return imagEigenvalues[i];
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getEigenvector(final int i)
+ throws InvalidMatrixException, ArrayIndexOutOfBoundsException {
+ return eigenvectors[i].copy();
+ }
+
+ /**
+ * Return the determinant of the matrix
+ * @return determinant of the matrix
+ */
+ public double getDeterminant() {
+ double determinant = 1;
+ for (double lambda : realEigenvalues) {
+ determinant *= lambda;
+ }
+ return determinant;
+ }
+
+ /** {@inheritDoc} */
+ public DecompositionSolver getSolver() {
+ 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;
+
+ /**
+ * Build 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;
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A &times; X - B|| is exactly 0.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ * @exception InvalidMatrixException
+ * if decomposed matrix is singular
+ */
+ public double[] solve(final double[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ if (b.length != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.length, 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 bp;
+
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A &times; X - B|| is exactly 0.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ * @exception InvalidMatrixException
+ * if decomposed matrix is singular
+ */
+ public RealVector solve(final RealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ if (b.getDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, 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);
+
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A &times; X - B|| is exactly 0.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ * @exception InvalidMatrixException
+ * if decomposed matrix is singular
+ */
+ public RealMatrix solve(final RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ if (b.getRowDimension() != m) {
+ throw MathRuntimeException
+ .createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), m,
+ "n");
+ }
+
+ final int nColB = b.getColumnDimension();
+ final double[][] bp = new double[m][nColB];
+ for (int k = 0; k < nColB; ++k) {
+ 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) * b.getEntry(j, k);
+ }
+ s /= realEigenvalues[i];
+ for (int j = 0; j < m; ++j) {
+ bp[j][k] += s * vData[j];
+ }
+ }
+ }
+
+ return MatrixUtils.createRealMatrix(bp);
+
+ }
+
+ /**
+ * Check if the decomposed matrix is non-singular.
+ * @return true if the decomposed matrix is non-singular
+ */
+ public boolean isNonSingular() {
+ for (int i = 0; i < realEigenvalues.length; ++i) {
+ if ((realEigenvalues[i] == 0) && (imagEigenvalues[i] == 0)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the inverse of the decomposed matrix.
+ * @return inverse matrix
+ * @throws InvalidMatrixException
+ * if decomposed matrix is singular
+ */
+ public RealMatrix getInverse() throws InvalidMatrixException {
+
+ 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);
+
+ }
+
+ }
+
+ /**
+ * Transform matrix to tridiagonal.
+ * @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 tri-diagonal form.
+ */
+ private void findEigenVectors(double[][] householderMatrix) {
+
+ double[][]z = householderMatrix.clone();
+ final int n = main.length;
+ realEigenvalues = new double[n];
+ imagEigenvalues = new double[n];
+ 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.0;
+
+ // Determine the largest main and secondary value in absolute term.
+ double maxAbsoluteValue=0.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.0) {
+ for (int i=0; i < n; i++) {
+ if (FastMath.abs(realEigenvalues[i])<=MathUtils.EPSILON*maxAbsoluteValue) {
+ realEigenvalues[i]=0.0;
+ }
+ if (FastMath.abs(e[i])<=MathUtils.EPSILON*maxAbsoluteValue) {
+ e[i]=0.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 InvalidMatrixException(
+ new MaxIterationsExceededException(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 = c * s;
+ } else {
+ s = p / q;
+ t = FastMath.sqrt(s * s + 1.0);
+ e[i + 1] = q * t;
+ c = 1.0 / t;
+ s = 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.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])<MathUtils.EPSILON*maxAbsoluteValue) {
+ realEigenvalues[i]=0.0;
+ }
+ }
+ }
+ eigenvectors = new ArrayRealVector[n];
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldDecompositionSolver.java b/src/main/java/org/apache/commons/math/linear/FieldDecompositionSolver.java
new file mode 100644
index 0000000..8238cc3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldDecompositionSolver.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.math.linear;
+
+import org.apache.commons.math.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>
+ * <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.</p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 781122 $ $Date: 2009-06-02 20:53:23 +0200 (mar. 02 juin 2009) $
+ * @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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ T[] solve(final T[] b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /** 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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ FieldVector<T> solve(final FieldVector<T> b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /** 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.</p>
+ * @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
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ FieldMatrix<T> solve(final FieldMatrix<T> b)
+ throws IllegalArgumentException, InvalidMatrixException;
+
+ /**
+ * 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 InvalidMatrixException if decomposed matrix is singular
+ */
+ FieldMatrix<T> getInverse()
+ throws InvalidMatrixException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldLUDecomposition.java b/src/main/java/org/apache/commons/math/linear/FieldLUDecomposition.java
new file mode 100644
index 0000000..cbdbadd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldLUDecomposition.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.math.linear;
+
+import org.apache.commons.math.FieldElement;
+
+/**
+ * An interface to classes that implement an algorithm to calculate the
+ * LU-decomposition of a real matrix.
+ * <p>The LU-decomposition of matrix A is a set of three matrices: P, L and U
+ * such that P&times;A = L&times;U. P is a rows permutation matrix that is used
+ * to rearrange the rows of A before so that it can be decomposed. L is a lower
+ * triangular matrix with unit diagonal terms and U is an upper triangular matrix.</p>
+ * <p>This interface is based on the class with similar name from the
+ * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.</p>
+ * <ul>
+ * <li>a {@link #getP() getP} method has been added,</li>
+ * <li>the <code>det</code> method has been renamed as {@link #getDeterminant()
+ * getDeterminant},</li>
+ * <li>the <code>getDoublePivot</code> method has been removed (but the int based
+ * {@link #getPivot() getPivot} method has been kept),</li>
+ * <li>the <code>solve</code> and <code>isNonSingular</code> methods have been replaced
+ * by a {@link #getSolver() getSolver} method and the equivalent methods provided by
+ * the returned {@link DecompositionSolver}.</li>
+ * </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>
+ * @version $Revision: 826627 $ $Date: 2009-10-19 12:27:47 +0200 (lun. 19 oct. 2009) $
+ * @since 2.0
+ */
+public interface FieldLUDecomposition<T extends FieldElement<T>> {
+
+ /**
+ * Returns the matrix L of the decomposition.
+ * <p>L is an lower-triangular matrix</p>
+ * @return the L matrix (or null if decomposed matrix is singular)
+ */
+ FieldMatrix<T> getL();
+
+ /**
+ * Returns the matrix U of the decomposition.
+ * <p>U is an upper-triangular matrix</p>
+ * @return the U matrix (or null if decomposed matrix is singular)
+ */
+ FieldMatrix<T> getU();
+
+ /**
+ * 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>
+ * <p>The positions of the 1 elements are given by the {@link #getPivot()
+ * pivot permutation vector}.</p>
+ * @return the P rows permutation matrix (or null if decomposed matrix is singular)
+ * @see #getPivot()
+ */
+ FieldMatrix<T> getP();
+
+ /**
+ * Returns the pivot permutation vector.
+ * @return the pivot permutation vector
+ * @see #getP()
+ */
+ int[] getPivot();
+
+ /**
+ * Return the determinant of the matrix
+ * @return determinant of the matrix
+ */
+ T getDeterminant();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in exact linear sense.
+ * @return a solver
+ */
+ FieldDecompositionSolver<T> getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldLUDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/FieldLUDecompositionImpl.java
new file mode 100644
index 0000000..44fba31
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldLUDecompositionImpl.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.math.linear;
+
+import java.lang.reflect.Array;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ * <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>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class FieldLUDecompositionImpl<T extends FieldElement<T>> implements FieldLUDecomposition<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.
+ * @exception NonSquareMatrixException if matrix is not square
+ */
+ public FieldLUDecompositionImpl(FieldMatrix<T> matrix)
+ throws NonSquareMatrixException {
+
+ 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);
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ public int[] getPivot() {
+ return pivot.clone();
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldDecompositionSolver<T> getSolver() {
+ return new Solver<T>(field, lu, pivot, singular);
+ }
+
+ /** Specialized solver. */
+ private static class Solver<T extends FieldElement<T>> implements FieldDecompositionSolver<T> {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6353105415121373022L;
+
+ /** 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 T[] solve(T[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = pivot.length;
+ if (b.length != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.length, m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ @SuppressWarnings("unchecked") // field is of type T
+ final T[] bp = (T[]) Array.newInstance(field.getZero().getClass(), m);
+
+ // Apply permutations to b
+ for (int row = 0; row < m; row++) {
+ bp[row] = b[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 bp;
+
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> solve(FieldVector<T> b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ try {
+ return solve((ArrayFieldVector<T>) b);
+ } catch (ClassCastException cce) {
+
+ final int m = pivot.length;
+ if (b.getDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.getDimension(), m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ @SuppressWarnings("unchecked") // field is of type T
+ final T[] bp = (T[]) Array.newInstance(field.getZero().getClass(), 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 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);
+
+ }
+ }
+
+ /** Solve the linear equation A &times; X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X such that A &times; X = B
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ public ArrayFieldVector<T> solve(ArrayFieldVector<T> b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ return new ArrayFieldVector<T>(solve(b.getDataRef()), false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> solve(FieldMatrix<T> b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = pivot.length;
+ if (b.getRowDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), m, "n");
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ final int nColB = b.getColumnDimension();
+
+ // Apply permutations to b
+ @SuppressWarnings("unchecked") // field is of type T
+ final T[][] bp = (T[][]) Array.newInstance(field.getZero().getClass(), new int[] { 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>(bp, false);
+
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getInverse() throws InvalidMatrixException {
+ 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/math/linear/FieldMatrix.java b/src/main/java/org/apache/commons/math/linear/FieldMatrix.java
new file mode 100644
index 0000000..98d82ae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldMatrix.java
@@ -0,0 +1,798 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * 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.</p>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public interface FieldMatrix<T extends FieldElement<T>> extends AnyMatrix {
+
+ /**
+ * Get the type of field elements of the matrix.
+ * @return 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 IllegalArgumentException if row or column dimension is not positive
+ * @since 2.0
+ */
+ FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension);
+
+ /**
+ * Returns a (deep) copy of this.
+ *
+ * @return matrix copy
+ */
+ FieldMatrix<T> copy();
+
+ /**
+ * Compute the sum of this and m.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ FieldMatrix<T> add(FieldMatrix<T> m) throws IllegalArgumentException;
+
+ /**
+ * Compute this minus m.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ FieldMatrix<T> subtract(FieldMatrix<T> m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result of adding d to each entry of this.
+ *
+ * @param d value to be added to each entry
+ * @return d + this
+ */
+ FieldMatrix<T> scalarAdd(T d);
+
+ /**
+ * Returns the result multiplying each entry of this by d.
+ *
+ * @param d value to multiply all entries by
+ * @return d * this
+ */
+ FieldMatrix<T> scalarMultiply(T d);
+
+ /**
+ * Returns the result of postmultiplying this by m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ FieldMatrix<T> multiply(FieldMatrix<T> m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result premultiplying this by <code>m</code>.
+ * @param m matrix to premultiply by
+ * @return m * this
+ * @throws IllegalArgumentException
+ * if rowDimension(this) != columnDimension(m)
+ */
+ FieldMatrix<T> preMultiply(FieldMatrix<T> m) throws IllegalArgumentException;
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ *
+ * @return 2-dimensional array of entries
+ */
+ T[][] getData();
+
+ /**
+ * 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
+ * @exception MatrixIndexException if the indices are not valid
+ */
+ FieldMatrix<T> getSubMatrix(int startRow, int endRow, int startColumn, int endColumn)
+ throws MatrixIndexException;
+
+ /**
+ * 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
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ FieldMatrix<T> getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws MatrixIndexException;
+
+ /**
+ * 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)
+ * @exception MatrixIndexException if the indices are not valid
+ * @exception IllegalArgumentException if the destination array is too small
+ */
+ void copySubMatrix(int startRow, int endRow, int startColumn, int endColumn,
+ T[][] destination)
+ throws MatrixIndexException, IllegalArgumentException;
+
+ /**
+ * 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)
+ * @exception MatrixIndexException if the indices are not valid
+ * @exception IllegalArgumentException if the destination array is too small
+ */
+ void copySubMatrix(int[] selectedRows, int[] selectedColumns, T[][] destination)
+ throws MatrixIndexException, IllegalArgumentException;
+
+ /**
+ * Replace the submatrix starting at <code>row, column</code> using data in
+ * the input <code>subMatrix</code> 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></p>
+ *
+ * @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 MatrixIndexException if subMatrix does not fit into this
+ * matrix from element in (row, column)
+ * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>subMatrix</code> is null
+ * @since 2.0
+ */
+ void setSubMatrix(T[][] subMatrix, int row, int column)
+ throws MatrixIndexException;
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row matrix
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ FieldMatrix<T> getRowMatrix(int row) throws MatrixIndexException;
+
+ /**
+ * 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 MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance row
+ */
+ void setRowMatrix(int row, FieldMatrix<T> matrix)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a column matrix. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column matrix
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ FieldMatrix<T> getColumnMatrix(int column) throws MatrixIndexException;
+
+ /**
+ * 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 MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance column
+ */
+ void setColumnMatrix(int column, FieldMatrix<T> matrix)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a vector. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row vector
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ FieldVector<T> getRowVector(int row) throws MatrixIndexException;
+
+ /**
+ * Sets the entries in row number <code>row</code>
+ * as a vector. Row indices start at 0.
+ *
+ * @param row the row to be set
+ * @param vector row vector (must have the same number of columns
+ * as the instance)
+ * @throws MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the vector dimension does not match one
+ * instance row
+ */
+ void setRowVector(int row, FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a vector. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column vector
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ FieldVector<T> getColumnVector(int column) throws MatrixIndexException;
+
+ /**
+ * Sets the entries in column number <code>column</code>
+ * as a vector. Column indices start at 0.
+ *
+ * @param column the column to be set
+ * @param vector column vector (must have the same number of rows as the instance)
+ * @throws MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the vector dimension does not match one
+ * instance column
+ */
+ void setColumnVector(int column, FieldVector<T> vector)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ T[] getRow(int row) throws MatrixIndexException;
+
+ /**
+ * 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 array row matrix (must have the same number of columns as the instance)
+ * @throws MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the array size does not match one
+ * instance row
+ */
+ void setRow(int row, T[] array)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param column the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ T[] getColumn(int column) throws MatrixIndexException;
+
+ /**
+ * 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 array column array (must have the same number of rows as the instance)
+ * @throws MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the array size does not match one
+ * instance column
+ */
+ void setColumn(int column, T[] array)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ */
+ T getEntry(int row, int column) throws MatrixIndexException;
+
+ /**
+ * Set the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void setEntry(int row, int column, T value) throws MatrixIndexException;
+
+ /**
+ * Change an entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 row,column
+ * @throws MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void addToEntry(int row, int column, T increment) throws MatrixIndexException;
+
+ /**
+ * Change an entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 row,column
+ * @throws MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void multiplyEntry(int row, int column, T factor) throws MatrixIndexException;
+
+ /**
+ * 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</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ T[] operate(T[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ FieldVector<T> operate(FieldVector<T> v) throws IllegalArgumentException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ T[] preMultiply(T[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ FieldVector<T> preMultiply(FieldVector<T> v) throws IllegalArgumentException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException 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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException 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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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)
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException 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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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)
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException 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 MatrixIndexException, MatrixVisitorException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math/linear/FieldMatrixChangingVisitor.java
new file mode 100644
index 0000000..c621975
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldMatrixChangingVisitor.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.math.linear;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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.</p>
+ * @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
+ * @throws MatrixVisitorException if something wrong occurs
+ */
+ T visit(int row, int column, T value)
+ throws MatrixVisitorException;
+
+ /**
+ * End visiting a matrix.
+ * <p>This method is called once after all entries of the matrix have been visited.</p>
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ T end();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math/linear/FieldMatrixPreservingVisitor.java
new file mode 100644
index 0000000..76e062e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldMatrixPreservingVisitor.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.math.linear;
+
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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.</p>
+ * @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
+ * @throws MatrixVisitorException if something wrong occurs
+ */
+ void visit(int row, int column, T value)
+ throws MatrixVisitorException;
+
+ /**
+ * End visiting a matrix.
+ * <p>This method is called once after all entries of the matrix have been visited.</p>
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ T end();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/FieldVector.java b/src/main/java/org/apache/commons/math/linear/FieldVector.java
new file mode 100644
index 0000000..c68281b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/FieldVector.java
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+
+/**
+ * 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>
+ * <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:
+ * </p>
+ * <pre>
+ * RealVector result = v.mapAddToSelf(3.0).mapTanToSelf().mapSquareToSelf();
+ * </pre>
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @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 this and v.
+ * @param v vector to be added
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> add(FieldVector<T> v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute the sum of this and v.
+ * @param v vector to be added
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> add(T[] v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute this minus v.
+ * @param v vector to be subtracted
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> subtract(FieldVector<T> v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute this minus v.
+ * @param v vector to be subtracted
+ * @return this + v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> subtract(T[] v)
+ throws IllegalArgumentException;
+
+ /**
+ * Map an addition operation to each entry.
+ * @param d value to be added to each entry
+ * @return this + d
+ */
+ FieldVector<T> mapAdd(T d);
+
+ /**
+ * Map an addition operation to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @param d value to be added to each entry
+ * @return for convenience, return this
+ */
+ FieldVector<T> mapAddToSelf(T d);
+
+ /**
+ * Map a subtraction operation to each entry.
+ * @param d value to be subtracted to each entry
+ * @return this - d
+ */
+ FieldVector<T> mapSubtract(T d);
+
+ /**
+ * Map a subtraction operation to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @param d value to be subtracted to each entry
+ * @return for convenience, return this
+ */
+ FieldVector<T> mapSubtractToSelf(T d);
+
+ /**
+ * Map a multiplication operation to each entry.
+ * @param d value to multiply all entries by
+ * @return this * d
+ */
+ FieldVector<T> mapMultiply(T d);
+
+ /**
+ * Map a multiplication operation to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @param d value to multiply all entries by
+ * @return for convenience, return this
+ */
+ FieldVector<T> mapMultiplyToSelf(T d);
+
+ /**
+ * Map a division operation to each entry.
+ * @param d value to divide all entries by
+ * @return this / d
+ */
+ FieldVector<T> mapDivide(T d);
+
+ /**
+ * Map a division operation to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @param d value to divide all entries by
+ * @return for convenience, return this
+ */
+ FieldVector<T> mapDivideToSelf(T d);
+
+ /**
+ * Map the 1/x function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ */
+ FieldVector<T> mapInv();
+
+ /**
+ * Map the 1/x function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ */
+ FieldVector<T> mapInvToSelf();
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> ebeMultiply(FieldVector<T> v) throws IllegalArgumentException;
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> ebeMultiply(T[] v) throws IllegalArgumentException;
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> ebeDivide(FieldVector<T> v) throws IllegalArgumentException;
+
+ /**
+ * 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 IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> ebeDivide(T[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns vector entries as a T array.
+ * @return T array of entries
+ */
+ T[] getData();
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ T dotProduct(FieldVector<T> v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ T dotProduct(T[] v)
+ throws IllegalArgumentException;
+
+ /** 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 v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> projection(FieldVector<T> v)
+ throws IllegalArgumentException;
+
+ /** 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 v
+ * @throws IllegalArgumentException if v is not the same size as this
+ */
+ FieldVector<T> projection(T[] v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute the outer product.
+ * @param v vector with which outer product should be computed
+ * @return the square matrix outer product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ FieldMatrix<T> outerProduct(FieldVector<T> v)
+ throws IllegalArgumentException;
+
+ /**
+ * Compute the outer product.
+ * @param v vector with which outer product should be computed
+ * @return the square matrix outer product between instance and v
+ * @exception IllegalArgumentException if v is not the same size as this
+ */
+ FieldMatrix<T> outerProduct(T[] v)
+ throws IllegalArgumentException;
+
+ /**
+ * Returns the entry in the specified index.
+ * <p>
+ * The index start at 0 and must be lesser than the size,
+ * otherwise a {@link MatrixIndexException} is thrown.
+ * </p>
+ * @param index index location of entry to be fetched
+ * @return vector entry at index
+ * @throws MatrixIndexException if the index is not valid
+ * @see #setEntry(int, FieldElement)
+ */
+ T getEntry(int index)
+ throws MatrixIndexException;
+
+ /**
+ * Set a single element.
+ * @param index element index.
+ * @param value new value for the element.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ * @see #getEntry(int)
+ */
+ void setEntry(int index, T value)
+ throws MatrixIndexException;
+
+ /**
+ * 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);
+
+ /**
+ * Construct a vector by appending a T array to this vector.
+ * @param a T array to append.
+ * @return a new vector
+ */
+ FieldVector<T> append(T[] a);
+
+ /**
+ * 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.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ */
+ FieldVector<T> getSubVector(int index, int n)
+ throws MatrixIndexException;
+
+ /**
+ * Set a set of consecutive elements.
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ * @see #setSubVector(int, FieldElement[])
+ */
+ void setSubVector(int index, FieldVector<T> v)
+ throws MatrixIndexException;
+
+ /**
+ * Set a set of consecutive elements.
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @exception MatrixIndexException if the index is
+ * inconsistent with vector size
+ * @see #setSubVector(int, FieldVector)
+ */
+ void setSubVector(int index, T[] v)
+ throws MatrixIndexException;
+
+ /**
+ * 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.</p>
+ * @return array containing a copy of vector elements
+ */
+ T[] toArray();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/InvalidMatrixException.java b/src/main/java/org/apache/commons/math/linear/InvalidMatrixException.java
new file mode 100644
index 0000000..403188d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/InvalidMatrixException.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.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Thrown when a system attempts an operation on a matrix, and
+ * that matrix does not satisfy the preconditions for the
+ * aforementioned operation.
+ * @version $Revision: 1073253 $ $Date: 2011-02-22 09:40:05 +0100 (mar. 22 févr. 2011) $
+ */
+public class InvalidMatrixException extends MathRuntimeException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -2068020346562029801L;
+
+ /**
+ * Construct an exception with the given message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.0
+ * @deprecated since 2.2 replaced by {@link #InvalidMatrixException(Localizable, Object...)}
+ */
+ @Deprecated
+ public InvalidMatrixException(final String pattern, final Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Construct an exception with the given message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public InvalidMatrixException(final Localizable pattern, final Object ... arguments) {
+ super(pattern, arguments);
+ }
+
+ /**
+ * Construct an exception with the given message.
+ * @param cause the exception or error that caused this exception
+ * to be thrown.
+ * @since 2.0
+ */
+ public InvalidMatrixException(final Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/LUDecomposition.java b/src/main/java/org/apache/commons/math/linear/LUDecomposition.java
new file mode 100644
index 0000000..41a61aa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/LUDecomposition.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.math.linear;
+
+
+/**
+ * An interface to classes that implement an algorithm to calculate the
+ * LU-decomposition of a real matrix.
+ * <p>The LU-decomposition of matrix A is a set of three matrices: P, L and U
+ * such that P&times;A = L&times;U. P is a rows permutation matrix that is used
+ * to rearrange the rows of A before so that it can be decomposed. L is a lower
+ * triangular matrix with unit diagonal terms and U is an upper triangular matrix.</p>
+ * <p>This interface is based on the class with similar name from the
+ * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.</p>
+ * <ul>
+ * <li>a {@link #getP() getP} method has been added,</li>
+ * <li>the <code>det</code> method has been renamed as {@link #getDeterminant()
+ * getDeterminant},</li>
+ * <li>the <code>getDoublePivot</code> method has been removed (but the int based
+ * {@link #getPivot() getPivot} method has been kept),</li>
+ * <li>the <code>solve</code> and <code>isNonSingular</code> methods have been replaced
+ * by a {@link #getSolver() getSolver} method and the equivalent methods provided by
+ * the returned {@link DecompositionSolver}.</li>
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
+ * @version $Revision: 826627 $ $Date: 2009-10-19 12:27:47 +0200 (lun. 19 oct. 2009) $
+ * @since 2.0
+ */
+public interface LUDecomposition {
+
+ /**
+ * Returns the matrix L of the decomposition.
+ * <p>L is an lower-triangular matrix</p>
+ * @return the L matrix (or null if decomposed matrix is singular)
+ */
+ RealMatrix getL();
+
+ /**
+ * Returns the matrix U of the decomposition.
+ * <p>U is an upper-triangular matrix</p>
+ * @return the U matrix (or null if decomposed matrix is singular)
+ */
+ RealMatrix getU();
+
+ /**
+ * 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>
+ * <p>The positions of the 1 elements are given by the {@link #getPivot()
+ * pivot permutation vector}.</p>
+ * @return the P rows permutation matrix (or null if decomposed matrix is singular)
+ * @see #getPivot()
+ */
+ RealMatrix getP();
+
+ /**
+ * Returns the pivot permutation vector.
+ * @return the pivot permutation vector
+ * @see #getP()
+ */
+ int[] getPivot();
+
+ /**
+ * Return the determinant of the matrix
+ * @return determinant of the matrix
+ */
+ double getDeterminant();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in exact linear sense.
+ * @return a solver
+ */
+ DecompositionSolver getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/LUDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/LUDecompositionImpl.java
new file mode 100644
index 0000000..e5c9bbf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/LUDecompositionImpl.java
@@ -0,0 +1,423 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.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: PA = LU, L is lower triangular, and U is
+ * upper triangular and P is a permutation matrix. All matrices are
+ * m&times;m.</p>
+ * <p>As shown by the presence of the P matrix, this decomposition is
+ * implemented using partial pivoting.</p>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class LUDecompositionImpl implements LUDecomposition {
+
+ /** Default bound to determine effective singularity in LU decomposition */
+ private static final double DEFAULT_TOO_SMALL = 10E-12;
+
+ /** Entries of LU decomposition. */
+ private double 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 RealMatrix cachedL;
+
+ /** Cached value of U. */
+ private RealMatrix cachedU;
+
+ /** Cached value of P. */
+ private RealMatrix cachedP;
+
+ /**
+ * Calculates the LU-decomposition of the given matrix.
+ * @param matrix The matrix to decompose.
+ * @exception InvalidMatrixException if matrix is not square
+ */
+ public LUDecompositionImpl(RealMatrix matrix)
+ throws InvalidMatrixException {
+ 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
+ * @exception NonSquareMatrixException if matrix is not square
+ */
+ public LUDecompositionImpl(RealMatrix matrix, double singularityThreshold)
+ throws NonSquareMatrixException {
+
+ 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++) {
+
+ double sum = 0;
+
+ // upper
+ for (int row = 0; row < col; row++) {
+ final double[] luRow = lu[row];
+ 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];
+ 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;
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ public int[] getPivot() {
+ return pivot.clone();
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ }
+ }
+
+ /** {@inheritDoc} */
+ 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 double[] solve(double[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = pivot.length;
+ if (b.length != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, b.length, 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[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 bp;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ try {
+ return solve((ArrayRealVector) b);
+ } catch (ClassCastException cce) {
+
+ final int m = pivot.length;
+ if (b.getDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH, 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);
+
+ }
+ }
+
+ /** Solve the linear equation A &times; X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X such that A &times; X = B
+ * @exception IllegalArgumentException if matrices dimensions don't match
+ * @exception InvalidMatrixException if decomposed matrix is singular
+ */
+ public ArrayRealVector solve(ArrayRealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ return new ArrayRealVector(solve(b.getDataRef()), false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int m = pivot.length;
+ if (b.getRowDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), m, "n");
+ }
+ 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);
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getInverse() throws InvalidMatrixException {
+ return solve(MatrixUtils.createRealIdentityMatrix(pivot.length));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/MatrixIndexException.java b/src/main/java/org/apache/commons/math/linear/MatrixIndexException.java
new file mode 100644
index 0000000..6d1e0a3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/MatrixIndexException.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.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Thrown when an operation addresses a matrix coordinate (row, col)
+ * which is outside of the dimensions of a matrix.
+ * @version $Revision: 1073255 $ $Date: 2011-02-22 09:42:06 +0100 (mar. 22 févr. 2011) $
+ */
+public class MatrixIndexException extends MathRuntimeException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 8120540015829487660L;
+
+ /**
+ * Constructs a new instance with specified formatted detail message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @deprecated as of 2.2 replaced by {@link #MatrixIndexException(Localizable, Object...)}
+ */
+ @Deprecated
+ public MatrixIndexException(final String pattern, final Object ... arguments) {
+ this(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new instance with specified formatted detail message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MatrixIndexException(final Localizable pattern, final Object ... arguments) {
+ super(pattern, arguments);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/MatrixUtils.java b/src/main/java/org/apache/commons/math/linear/MatrixUtils.java
new file mode 100644
index 0000000..4917b89
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/MatrixUtils.java
@@ -0,0 +1,957 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.fraction.BigFraction;
+import org.apache.commons.math.fraction.Fraction;
+
+/**
+ * A collection of static methods that operate on or return matrices.
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class MatrixUtils {
+
+ /**
+ * 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>
+ * <p>The matrix elements are all set to 0.0.</p>
+ * @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>
+ * <p>The matrix elements are all set to field.getZero().</p>
+ * @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>
+ * <p>The input array is copied, not referenced.</p>
+ *
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if either <code>data</code> or
+ * <code>data[0]</code> is null
+ * @see #createRealMatrix(int, int)
+ */
+ public static RealMatrix createRealMatrix(double[][] data) {
+ 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>
+ * <p>The input array is copied, not referenced.</p>
+ * @param <T> the type of the field elements
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if either <code>data</code> or
+ * <code>data[0]</code> is null
+ * @see #createFieldMatrix(Field, int, int)
+ * @since 2.0
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createFieldMatrix(T[][] data) {
+ 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();
+ @SuppressWarnings("unchecked") // zero is type T
+ final T[][] d = (T[][]) Array.newInstance(zero.getClass(), new int[] { 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>(d, false);
+ }
+
+ /**
+ * 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
+ * @deprecated since 2.0, replaced by {@link #createFieldIdentityMatrix(Field, int)}
+ */
+ @Deprecated
+ public static BigMatrix createBigIdentityMatrix(int dimension) {
+ final BigDecimal[][] d = new BigDecimal[dimension][dimension];
+ for (int row = 0; row < dimension; row++) {
+ final BigDecimal[] dRow = d[row];
+ Arrays.fill(dRow, BigMatrixImpl.ZERO);
+ dRow[row] = BigMatrixImpl.ONE;
+ }
+ return new BigMatrixImpl(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;
+ }
+
+ /**
+ * Returns a {@link BigMatrix} whose entries are the the values in the
+ * the input array. The input array is copied, not referenced.
+ *
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if data is null
+ * @deprecated since 2.0 replaced by {@link #createFieldMatrix(FieldElement[][])}
+ */
+ @Deprecated
+ public static BigMatrix createBigMatrix(double[][] data) {
+ return new BigMatrixImpl(data);
+ }
+
+ /**
+ * Returns a {@link BigMatrix} whose entries are the the values in the
+ * the input array. The input array is copied, not referenced.
+ *
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if data is null
+ * @deprecated since 2.0 replaced by {@link #createFieldMatrix(FieldElement[][])}
+ */
+ @Deprecated
+ public static BigMatrix createBigMatrix(BigDecimal[][] data) {
+ return new BigMatrixImpl(data);
+ }
+
+ /**
+ * Returns a {@link BigMatrix} whose entries are the the values in the
+ * the input array.
+ * <p>If an array is built specially in order to be embedded in a
+ * BigMatrix and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param data data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @return BigMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>data</code> is null
+ * @see #createRealMatrix(double[][])
+ * @deprecated since 2.0 replaced by {@link #createFieldMatrix(FieldElement[][])}
+ */
+ @Deprecated
+ public static BigMatrix createBigMatrix(BigDecimal[][] data, boolean copyArray) {
+ return new BigMatrixImpl(data, copyArray);
+ }
+
+ /**
+ * Returns a {@link BigMatrix} whose entries are the the values in the
+ * the input array. The input array is copied, not referenced.
+ *
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws IllegalArgumentException if <code>data</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if data is null
+ * @deprecated since 2.0 replaced by {@link #createFieldMatrix(FieldElement[][])}
+ */
+ @Deprecated
+ public static BigMatrix createBigMatrix(String[][] data) {
+ return new BigMatrixImpl(data);
+ }
+
+ /**
+ * Creates a {@link RealVector} using the data from the input array.
+ *
+ * @param data the input data
+ * @return a data.length RealVector
+ * @throws IllegalArgumentException if <code>data</code> is empty
+ * @throws NullPointerException if <code>data</code>is null
+ */
+ public static RealVector createRealVector(double[] data) {
+ 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 IllegalArgumentException if <code>data</code> is empty
+ * @throws NullPointerException if <code>data</code>is null
+ */
+ public static <T extends FieldElement<T>> FieldVector<T> createFieldVector(final T[] data) {
+ return new ArrayFieldVector<T>(data, true);
+ }
+
+ /**
+ * Creates 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 IllegalArgumentException if <code>rowData</code> is empty
+ * @throws NullPointerException if <code>rowData</code>is null
+ */
+ public static RealMatrix createRowRealMatrix(double[] rowData) {
+ 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;
+ }
+
+ /**
+ * Creates 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 IllegalArgumentException if <code>rowData</code> is empty
+ * @throws NullPointerException if <code>rowData</code>is null
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T>
+ createRowFieldMatrix(final T[] rowData) {
+ final int nCols = rowData.length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(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 row {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param rowData the input row data
+ * @return a 1 x rowData.length BigMatrix
+ * @throws IllegalArgumentException if <code>rowData</code> is empty
+ * @throws NullPointerException if <code>rowData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createRowFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createRowBigMatrix(double[] rowData) {
+ final int nCols = rowData.length;
+ final BigDecimal[][] data = new BigDecimal[1][nCols];
+ for (int i = 0; i < nCols; ++i) {
+ data[0][i] = new BigDecimal(rowData[i]);
+ }
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * Creates a row {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param rowData the input row data
+ * @return a 1 x rowData.length BigMatrix
+ * @throws IllegalArgumentException if <code>rowData</code> is empty
+ * @throws NullPointerException if <code>rowData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createRowFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createRowBigMatrix(BigDecimal[] rowData) {
+ final int nCols = rowData.length;
+ final BigDecimal[][] data = new BigDecimal[1][nCols];
+ System.arraycopy(rowData, 0, data[0], 0, nCols);
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * Creates a row {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param rowData the input row data
+ * @return a 1 x rowData.length BigMatrix
+ * @throws IllegalArgumentException if <code>rowData</code> is empty
+ * @throws NullPointerException if <code>rowData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createRowFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createRowBigMatrix(String[] rowData) {
+ final int nCols = rowData.length;
+ final BigDecimal[][] data = new BigDecimal[1][nCols];
+ for (int i = 0; i < nCols; ++i) {
+ data[0][i] = new BigDecimal(rowData[i]);
+ }
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * 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 IllegalArgumentException if <code>columnData</code> is empty
+ * @throws NullPointerException if <code>columnData</code>is null
+ */
+ public static RealMatrix createColumnRealMatrix(double[] columnData) {
+ 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 IllegalArgumentException if <code>columnData</code> is empty
+ * @throws NullPointerException if <code>columnData</code>is null
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T>
+ createColumnFieldMatrix(final T[] columnData) {
+ final int nRows = columnData.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(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;
+ }
+
+ /**
+ * Creates a column {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param columnData the input column data
+ * @return a columnData x 1 BigMatrix
+ * @throws IllegalArgumentException if <code>columnData</code> is empty
+ * @throws NullPointerException if <code>columnData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createColumnFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createColumnBigMatrix(double[] columnData) {
+ final int nRows = columnData.length;
+ final BigDecimal[][] data = new BigDecimal[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = new BigDecimal(columnData[row]);
+ }
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * Creates a column {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param columnData the input column data
+ * @return a columnData x 1 BigMatrix
+ * @throws IllegalArgumentException if <code>columnData</code> is empty
+ * @throws NullPointerException if <code>columnData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createColumnFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createColumnBigMatrix(BigDecimal[] columnData) {
+ final int nRows = columnData.length;
+ final BigDecimal[][] data = new BigDecimal[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = columnData[row];
+ }
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * Creates a column {@link BigMatrix} using the data from the input
+ * array.
+ *
+ * @param columnData the input column data
+ * @return a columnData x 1 BigMatrix
+ * @throws IllegalArgumentException if <code>columnData</code> is empty
+ * @throws NullPointerException if <code>columnData</code>is null
+ * @deprecated since 2.0 replaced by {@link #createColumnFieldMatrix(FieldElement[])}
+ */
+ @Deprecated
+ public static BigMatrix createColumnBigMatrix(String[] columnData) {
+ int nRows = columnData.length;
+ final BigDecimal[][] data = new BigDecimal[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = new BigDecimal(columnData[row]);
+ }
+ return new BigMatrixImpl(data, false);
+ }
+
+ /**
+ * Check if a row index is valid.
+ * @param m matrix containing the submatrix
+ * @param row row index to check
+ * @exception MatrixIndexException if index is not valid
+ */
+ public static void checkRowIndex(final AnyMatrix m, final int row) {
+ if (row < 0 || row >= m.getRowDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.ROW_INDEX_OUT_OF_RANGE,
+ row, 0, m.getRowDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if a column index is valid.
+ * @param m matrix containing the submatrix
+ * @param column column index to check
+ * @exception MatrixIndexException if index is not valid
+ */
+ public static void checkColumnIndex(final AnyMatrix m, final int column)
+ throws MatrixIndexException {
+ if (column < 0 || column >= m.getColumnDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.COLUMN_INDEX_OUT_OF_RANGE,
+ column, 0, m.getColumnDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid.
+ * Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param m matrix containing the submatrix
+ * @param startRow Initial row index
+ * @param endRow Final row index
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @exception MatrixIndexException if the indices are not valid
+ */
+ public static void checkSubMatrixIndex(final AnyMatrix m,
+ final int startRow, final int endRow,
+ final int startColumn, final int endColumn) {
+ checkRowIndex(m, startRow);
+ checkRowIndex(m, endRow);
+ if (startRow > endRow) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
+ startRow, endRow);
+ }
+
+ checkColumnIndex(m, startColumn);
+ checkColumnIndex(m, endColumn);
+ if (startColumn > endColumn) {
+ throw new MatrixIndexException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
+ startColumn, endColumn);
+ }
+
+
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid.
+ * Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param m matrix containing the submatrix
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ public static void checkSubMatrixIndex(final AnyMatrix m,
+ final int[] selectedRows, final int[] selectedColumns)
+ throws MatrixIndexException {
+ if (selectedRows.length * selectedColumns.length == 0) {
+ if (selectedRows.length == 0) {
+ throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
+ }
+ throw new MatrixIndexException(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
+ * @exception IllegalArgumentException if matrices are not addition compatible
+ */
+ public static void checkAdditionCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws IllegalArgumentException {
+ if ((left.getRowDimension() != right.getRowDimension()) ||
+ (left.getColumnDimension() != right.getColumnDimension())) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_ADDITION_COMPATIBLE_MATRICES,
+ 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
+ * @exception IllegalArgumentException if matrices are not subtraction compatible
+ */
+ public static void checkSubtractionCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws IllegalArgumentException {
+ if ((left.getRowDimension() != right.getRowDimension()) ||
+ (left.getColumnDimension() != right.getColumnDimension())) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_SUBTRACTION_COMPATIBLE_MATRICES,
+ 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
+ * @exception IllegalArgumentException if matrices are not multiplication compatible
+ */
+ public static void checkMultiplicationCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws IllegalArgumentException {
+ if (left.getColumnDimension() != right.getRowDimension()) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_MULTIPLICATION_COMPATIBLE_MATRICES,
+ left.getRowDimension(), left.getColumnDimension(),
+ right.getRowDimension(), right.getColumnDimension());
+ }
+ }
+
+ /**
+ * Convert a {@link FieldMatrix}/{@link Fraction} matrix to a {@link RealMatrix}.
+ * @param m matrix to convert
+ * @return 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. */
+ public 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 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 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. */
+ public 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 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>
+ * <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>
+ * </p>
+ *
+ * @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.
+ * </p>
+ * @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>
+ * <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>
+ * </p>
+ *
+ * @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.
+ * </p>
+ * @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;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/MatrixVisitorException.java b/src/main/java/org/apache/commons/math/linear/MatrixVisitorException.java
new file mode 100644
index 0000000..de25f16
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/MatrixVisitorException.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.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * Thrown when a visitor encounters an error while processing a matrix entry.
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class MatrixVisitorException extends MathRuntimeException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3814333035048617048L;
+
+ /**
+ * Constructs a new instance with specified formatted detail message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ */
+ public MatrixVisitorException(final String pattern, final Object[] arguments) {
+ super(new DummyLocalizable(pattern), arguments);
+ }
+
+ /**
+ * Constructs a new instance with specified formatted detail message.
+ * @param pattern format specifier
+ * @param arguments format arguments
+ * @since 2.2
+ */
+ public MatrixVisitorException(final Localizable pattern, final Object[] arguments) {
+ super(pattern, arguments);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/NonSquareMatrixException.java b/src/main/java/org/apache/commons/math/linear/NonSquareMatrixException.java
new file mode 100644
index 0000000..291c0e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/NonSquareMatrixException.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.math.linear;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+
+/**
+ * Thrown when an operation defined only for square matrices is applied to non-square ones.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class NonSquareMatrixException extends InvalidMatrixException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8996207526636673730L;
+
+ /**
+ * Construct an exception with the given message.
+ * @param rows number of rows of the faulty matrix
+ * @param columns number of columns of the faulty matrix
+ */
+ public NonSquareMatrixException(final int rows, final int columns) {
+ super(LocalizedFormats.NON_SQUARE_MATRIX, rows, columns);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/NotPositiveDefiniteMatrixException.java b/src/main/java/org/apache/commons/math/linear/NotPositiveDefiniteMatrixException.java
new file mode 100644
index 0000000..f7ede55
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/NotPositiveDefiniteMatrixException.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.math.linear;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown when a matrix expected to
+ * be positive definite is not.
+ *
+ * @since 1.2
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+
+public class NotPositiveDefiniteMatrixException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4122929125438624648L;
+
+ /** Simple constructor.
+ * build an exception with a default message.
+ */
+ public NotPositiveDefiniteMatrixException() {
+ super(LocalizedFormats.NOT_POSITIVE_DEFINITE_MATRIX);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/NotSymmetricMatrixException.java b/src/main/java/org/apache/commons/math/linear/NotSymmetricMatrixException.java
new file mode 100644
index 0000000..e422079
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/NotSymmetricMatrixException.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.math.linear;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown when a matrix expected to
+ * be symmetric is not
+ *
+ * @since 2.0
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+
+public class NotSymmetricMatrixException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -7012803946709786097L;
+
+ /** Simple constructor.
+ * build an exception with a default message.
+ */
+ public NotSymmetricMatrixException() {
+ super(LocalizedFormats.NOT_SYMMETRIC_MATRIX);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/OpenMapRealMatrix.java b/src/main/java/org/apache/commons/math/linear/OpenMapRealMatrix.java
new file mode 100644
index 0000000..e651320
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/OpenMapRealMatrix.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.util.OpenIntToDoubleHashMap;
+
+/**
+ * Sparse matrix implementation based on an open addressed map.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @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
+ */
+ public OpenMapRealMatrix(int rowDimension, int columnDimension) {
+ super(rowDimension, columnDimension);
+ 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} */
+ @Override
+ public OpenMapRealMatrix createMatrix(int rowDimension, int columnDimension)
+ throws IllegalArgumentException {
+ return new OpenMapRealMatrix(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealMatrix add(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return add((OpenMapRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return (OpenMapRealMatrix) super.add(m);
+ }
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public OpenMapRealMatrix add(OpenMapRealMatrix m) throws IllegalArgumentException {
+
+ // safety check
+ 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 IllegalArgumentException {
+ try {
+ return subtract((OpenMapRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return (OpenMapRealMatrix) super.subtract(m);
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this - m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public OpenMapRealMatrix subtract(OpenMapRealMatrix m) throws IllegalArgumentException {
+
+ // safety check
+ 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 RealMatrix multiply(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return multiply((OpenMapRealMatrix) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ 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;
+
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public OpenMapRealMatrix multiply(OpenMapRealMatrix m) throws IllegalArgumentException {
+
+ // 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 MatrixIndexException {
+ 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 MatrixIndexException {
+ 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 MatrixIndexException {
+ 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 MatrixIndexException {
+ 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/math/linear/OpenMapRealVector.java b/src/main/java/org/apache/commons/math/linear/OpenMapRealVector.java
new file mode 100644
index 0000000..b1d1912
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/OpenMapRealVector.java
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.OpenIntToDoubleHashMap;
+import org.apache.commons.math.util.OpenIntToDoubleHashMap.Iterator;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap} backing store.
+ * @version $Revision: 1073262 $ $Date: 2011-02-22 10:02:25 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+*/
+public class OpenMapRealVector extends AbstractRealVector implements SparseRealVector, 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.
+ * <p>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</code> method ({@link #append(double)}, {@link
+ * #append(double[])}, {@link #append(RealVector)}) to gather data
+ * into this vector.</p>
+ */
+ public OpenMapRealVector() {
+ this(0, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Construct a (dimension)-length vector of zeros.
+ * @param dimension size of the vector
+ */
+ public OpenMapRealVector(int dimension) {
+ this(dimension, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Construct a (dimension)-length vector of zeros, specifying zero tolerance.
+ * @param dimension Size of the vector
+ * @param epsilon The tolerance for having 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 The original vector
+ * @param resize The amount to resize it
+ */
+ 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 The 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 The size of the vector
+ * @param expectedSize The expected number of non-zero entries
+ * @param epsilon The tolerance for having a value considered zero
+ */
+ public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
+ virtualSize = dimension;
+ entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Create from a double 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 a double array, specifying zero tolerance.
+ * Only non-zero entries will be stored
+ * @param values The set of values to create from
+ * @param epsilon The tolerance for having a value 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 a Double 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 a Double array.
+ * Only non-zero entries will be stored
+ * @param values The set of values to create from
+ * @param epsilon The tolerance for having a value 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 The instance to copy from
+ */
+ public OpenMapRealVector(OpenMapRealVector v) {
+ virtualSize = v.getDimension();
+ entries = new OpenIntToDoubleHashMap(v.getEntries());
+ epsilon = v.epsilon;
+ }
+
+ /**
+ * Generic copy constructor.
+ * @param v The 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 entries of this instance
+ */
+ private OpenIntToDoubleHashMap getEntries() {
+ return entries;
+ }
+
+ /**
+ * Determine if this value is within epsilon of zero.
+ * @param value The value to test
+ * @return <code>true</code> if this value is within epsilon to zero, <code>false</code> otherwise
+ * @since 2.1
+ */
+ protected boolean isDefaultValue(double value) {
+ return FastMath.abs(value) < epsilon;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector add(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return add((OpenMapRealVector) v);
+ } else {
+ return super.add(v);
+ }
+ }
+
+ /**
+ * Optimized method to add two OpenMapRealVectors. Copies the larger vector, iterates over the smaller.
+ * @param v Vector to add with
+ * @return The sum of <code>this</code> with <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public OpenMapRealVector add(OpenMapRealVector v) throws IllegalArgumentException{
+ 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</code> 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} */
+ public OpenMapRealVector append(RealVector v) {
+ if (v instanceof OpenMapRealVector) {
+ return append((OpenMapRealVector) v);
+ }
+ return append(v.getData());
+ }
+
+ /** {@inheritDoc} */
+ public OpenMapRealVector append(double d) {
+ OpenMapRealVector res = new OpenMapRealVector(this, 1);
+ res.setEntry(virtualSize, d);
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public OpenMapRealVector append(double[] a) {
+ OpenMapRealVector res = new OpenMapRealVector(this, a.length);
+ for (int i = 0; i < a.length; i++) {
+ res.setEntry(i + virtualSize, a[i]);
+ }
+ return res;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 2.1
+ */
+ @Override
+ public OpenMapRealVector copy() {
+ return new OpenMapRealVector(this);
+ }
+
+ /**
+ * Optimized method to compute the dot product with an OpenMapRealVector.
+ * Iterates over the smaller of the two.
+ * @param v The vector to compute the dot product with
+ * @return The dot product of <code>this</code> and <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public double dotProduct(OpenMapRealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ boolean thisIsSmaller = entries.size() < v.entries.size();
+ Iterator iter = thisIsSmaller ? entries.iterator() : v.entries.iterator();
+ OpenIntToDoubleHashMap larger = thisIsSmaller ? v.entries : entries;
+ double d = 0;
+ while(iter.hasNext()) {
+ iter.advance();
+ d += iter.value() * larger.get(iter.key());
+ }
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double dotProduct(RealVector v) throws IllegalArgumentException {
+ if(v instanceof OpenMapRealVector) {
+ return dotProduct((OpenMapRealVector)v);
+ } else {
+ return super.dotProduct(v);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public OpenMapRealVector ebeDivide(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value() / v.getEntry(iter.key()));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector ebeDivide(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value() / v[iter.key()]);
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public OpenMapRealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector ebeMultiply(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value() * v[iter.key()]);
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public OpenMapRealVector getSubVector(int index, int n) throws MatrixIndexException {
+ checkIndex(index);
+ 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 double[] getData() {
+ double[] res = new double[virtualSize];
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res[iter.key()] = iter.value();
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return virtualSize;
+ }
+
+ /**
+ * Optimized method to compute distance.
+ * @param v The vector to compute distance to
+ * @return The distance from <code>this</code> and <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public double getDistance(OpenMapRealVector v) throws IllegalArgumentException {
+ 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 IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getDistance((OpenMapRealVector) v);
+ }
+ return getDistance(v.getData());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDistance(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double res = 0;
+ for (int i = 0; i < v.length; i++) {
+ double delta = entries.get(i) - v[i];
+ res += delta * delta;
+ }
+ return FastMath.sqrt(res);
+ }
+
+ /** {@inheritDoc} */
+ public double getEntry(int index) throws MatrixIndexException {
+ checkIndex(index);
+ return entries.get(index);
+ }
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ */
+ public double getL1Distance(OpenMapRealVector v) {
+ 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 IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getL1Distance((OpenMapRealVector) v);
+ }
+ return getL1Distance(v.getData());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Distance(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double max = 0;
+ for (int i = 0; i < v.length; i++) {
+ double delta = FastMath.abs(getEntry(i) - v[i]);
+ max += delta;
+ }
+ return max;
+ }
+
+ /**
+ * Optimized method to compute LInfDistance.
+ * @param v The vector to compute from
+ * @return the LInfDistance
+ */
+ private double getLInfDistance(OpenMapRealVector v) {
+ 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)) {
+ if (iter.value() > max) {
+ max = iter.value();
+ }
+ }
+ }
+ return max;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getLInfDistance((OpenMapRealVector) v);
+ }
+ return getLInfDistance(v.getData());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ double max = 0;
+ for (int i = 0; i < v.length; i++) {
+ double delta = FastMath.abs(getEntry(i) - v[i]);
+ if (delta > max) {
+ max = delta;
+ }
+ }
+ return max;
+ }
+
+ /** {@inheritDoc} */
+ 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} */
+ 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 RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ RealMatrix res = new OpenMapRealMatrix(virtualSize, virtualSize);
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int row = iter.key();
+ double value = iter.value();
+ for (int col = 0; col < virtualSize; col++) {
+ res.setEntry(row, col, value * v[col]);
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector projection(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ return v.mapMultiply(dotProduct(v) / v.dotProduct(v));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector projection(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ return (OpenMapRealVector) projection(new OpenMapRealVector(v));
+ }
+
+ /** {@inheritDoc} */
+ public void setEntry(int index, double value) throws MatrixIndexException {
+ 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 MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ setSubVector(index, v.getData());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubVector(int index, double[] v) throws MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ for (int i = 0; i < v.length; i++) {
+ setEntry(i + index, v[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 The vector to subtract from <code>this</code>
+ * @return The difference of <code>this</code> and <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public OpenMapRealVector subtract(OpenMapRealVector v) throws IllegalArgumentException{
+ 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 OpenMapRealVector subtract(RealVector v) throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return subtract((OpenMapRealVector) v);
+ }
+ return subtract(v.getData());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector subtract(double[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ for (int i = 0; i < v.length; i++) {
+ if (entries.containsKey(i)) {
+ res.setEntry(i, entries.get(i) - v[i]);
+ } else {
+ res.setEntry(i, -v[i]);
+ }
+ }
+ return res;
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector unitVector() {
+ OpenMapRealVector res = copy();
+ res.unitize();
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unitize() {
+ double norm = getNorm();
+ if (isDefaultValue(norm)) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ entries.put(iter.key(), iter.value() / norm);
+ }
+
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] toArray() {
+ return getData();
+ }
+
+ /** {@inheritDoc}
+ * <p> 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()}.</p>
+ */
+ @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;
+ }
+
+ /**
+ * <p> 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}.</p>
+ * {@inheritDoc}
+ */
+ @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.
+ * @deprecated as of 2.2 replaced by the correctly spelled {@link #getSparsity()}
+ */
+ @Deprecated
+ public double getSparcity() {
+ return getSparsity();
+ }
+
+ /**
+ *
+ * @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</code> optimized for OpenMap.
+ * <p>This implementation does not allow arbitrary calls to <code>setIndex</code>
+ * since the order that 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.
+ * <p>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/math/linear/QRDecomposition.java b/src/main/java/org/apache/commons/math/linear/QRDecomposition.java
new file mode 100644
index 0000000..2f58e05
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/QRDecomposition.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.math.linear;
+
+
+/**
+ * An interface to classes that implement an algorithm to calculate the
+ * QR-decomposition of a real matrix.
+ * <p>This interface 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:</p>
+ * <ul>
+ * <li>a {@link #getQT() getQT} method has been added,</li>
+ * <li>the <code>solve</code> and <code>isFullRank</code> methods have been replaced
+ * by a {@link #getSolver() getSolver} method and the equivalent methods provided by
+ * the returned {@link DecompositionSolver}.</li>
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/QRDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/QR_decomposition">Wikipedia</a>
+ * @version $Revision: 826627 $ $Date: 2009-10-19 12:27:47 +0200 (lun. 19 oct. 2009) $
+ * @since 1.2
+ */
+public interface QRDecomposition {
+
+ /**
+ * Returns the matrix R of the decomposition.
+ * <p>R is an upper-triangular matrix</p>
+ * @return the R matrix
+ */
+ RealMatrix getR();
+
+ /**
+ * Returns the matrix Q of the decomposition.
+ * <p>Q is an orthogonal matrix</p>
+ * @return the Q matrix
+ */
+ RealMatrix getQ();
+
+ /**
+ * Returns the transpose of the matrix Q of the decomposition.
+ * <p>Q is an orthogonal matrix</p>
+ * @return the Q matrix
+ */
+ RealMatrix getQT();
+
+ /**
+ * 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.</p>
+ * @return a matrix containing the Householder reflector vectors
+ */
+ RealMatrix getH();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ * @return a solver
+ */
+ DecompositionSolver getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/QRDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/QRDecompositionImpl.java
new file mode 100644
index 0000000..7356a8a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/QRDecompositionImpl.java
@@ -0,0 +1,453 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * 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>
+ * <p>This class compute the decomposition using Householder reflectors.</p>
+ * <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>
+ *
+ * @see <a href="http://mathworld.wolfram.com/QRDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/QR_decomposition">Wikipedia</a>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 1.2
+ */
+public class QRDecompositionImpl implements 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.</p>
+ */
+ 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;
+
+ /**
+ * Calculates the QR-decomposition of the given matrix.
+ * @param matrix The matrix to decompose.
+ */
+ public QRDecompositionImpl(RealMatrix matrix) {
+
+ 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;
+
+ /*
+ * The QR decomposition of a matrix A is calculated using Householder
+ * reflectors by repeating the following operations to each minor
+ * A(minor,minor) of A:
+ */
+ for (int minor = 0; minor < FastMath.min(m, n); minor++) {
+
+ final double[] qrtMinor = qrt[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 < m; 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 < n; col++) {
+ final double[] qrtCol = qrt[col];
+ double alpha = 0;
+ for (int row = minor; row < m; row++) {
+ alpha -= qrtCol[row] * qrtMinor[row];
+ }
+ alpha /= a * qrtMinor[minor];
+
+ // Subtract the column vector alpha*v from x.
+ for (int row = minor; row < m; row++) {
+ qrtCol[row] -= alpha * qrtMinor[row];
+ }
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ cachedR = MatrixUtils.createRealMatrix(m, n);
+
+ // copy the diagonal from rDiag and the upper triangle of qr
+ for (int row = FastMath.min(m, n) - 1; row >= 0; row--) {
+ cachedR.setEntry(row, row, rDiag[row]);
+ for (int col = row + 1; col < n; col++) {
+ cachedR.setEntry(row, col, qrt[col][row]);
+ }
+ }
+
+ }
+
+ // return the cached matrix
+ return cachedR;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getQ() {
+ if (cachedQ == null) {
+ cachedQ = getQT().transpose();
+ }
+ return cachedQ;
+ }
+
+ /** {@inheritDoc} */
+ 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;
+ cachedQT = MatrixUtils.createRealMatrix(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--) {
+ cachedQT.setEntry(minor, minor, 1.0);
+ }
+
+ for (int minor = FastMath.min(m, n)-1; minor >= 0; minor--){
+ final double[] qrtMinor = qrt[minor];
+ cachedQT.setEntry(minor, minor, 1.0);
+ if (qrtMinor[minor] != 0.0) {
+ for (int col = minor; col < m; col++) {
+ double alpha = 0;
+ for (int row = minor; row < m; row++) {
+ alpha -= cachedQT.getEntry(col, row) * qrtMinor[row];
+ }
+ alpha /= rDiag[minor] * qrtMinor[minor];
+
+ for (int row = minor; row < m; row++) {
+ cachedQT.addToEntry(col, row, -alpha * qrtMinor[row]);
+ }
+ }
+ }
+ }
+
+ }
+
+ // return the cached matrix
+ return cachedQT;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getH() {
+
+ if (cachedH == null) {
+
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ cachedH = MatrixUtils.createRealMatrix(m, n);
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < FastMath.min(i + 1, n); ++j) {
+ cachedH.setEntry(i, j, qrt[j][i] / -rDiag[j]);
+ }
+ }
+
+ }
+
+ // return the cached matrix
+ return cachedH;
+
+ }
+
+ /** {@inheritDoc} */
+ public DecompositionSolver getSolver() {
+ return new Solver(qrt, rDiag);
+ }
+
+ /** 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.</p>
+ */
+ private final double[][] qrt;
+
+ /** The diagonal elements of R. */
+ private final double[] rDiag;
+
+ /**
+ * Build a solver from decomposed matrix.
+ * @param qrt packed TRANSPOSED representation of the QR decomposition
+ * @param rDiag diagonal elements of R
+ */
+ private Solver(final double[][] qrt, final double[] rDiag) {
+ this.qrt = qrt;
+ this.rDiag = rDiag;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+
+ for (double diag : rDiag) {
+ if (diag == 0) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /** {@inheritDoc} */
+ public double[] solve(double[] b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ if (b.length != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ b.length, m);
+ }
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final double[] x = new double[n];
+ final double[] y = b.clone();
+
+ // 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 x;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ try {
+ return solve((ArrayRealVector) b);
+ } catch (ClassCastException cce) {
+ return new ArrayRealVector(solve(b.getData()), false);
+ }
+ }
+
+ /** Solve the linear equation A &times; X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @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 IllegalArgumentException if matrices dimensions don't match
+ * @throws InvalidMatrixException if decomposed matrix is singular
+ */
+ public ArrayRealVector solve(ArrayRealVector b)
+ throws IllegalArgumentException, InvalidMatrixException {
+ return new ArrayRealVector(solve(b.getDataRef()), false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b)
+ throws IllegalArgumentException, InvalidMatrixException {
+
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ if (b.getRowDimension() != m) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ b.getRowDimension(), b.getColumnDimension(), m, "n");
+ }
+ 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} */
+ public RealMatrix getInverse()
+ throws InvalidMatrixException {
+ return solve(MatrixUtils.createRealIdentityMatrix(rDiag.length));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealMatrix.java b/src/main/java/org/apache/commons/math/linear/RealMatrix.java
new file mode 100644
index 0000000..e025fd4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealMatrix.java
@@ -0,0 +1,871 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+
+/**
+ * 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.</p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+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 IllegalArgumentException if row or column dimension is not positive
+ * @since 2.0
+ */
+ RealMatrix createMatrix(final int rowDimension, final int columnDimension);
+
+ /**
+ * Returns a (deep) copy of this.
+ *
+ * @return matrix copy
+ */
+ RealMatrix copy();
+
+ /**
+ * Compute the sum of this and m.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ RealMatrix add(RealMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Compute this minus m.
+ *
+ * @param m matrix to be subtracted
+ * @return this - m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ RealMatrix subtract(RealMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result of adding d to each entry of this.
+ *
+ * @param d value to be added to each entry
+ * @return d + this
+ */
+ RealMatrix scalarAdd(double d);
+
+ /**
+ * Returns the result multiplying each entry of this by d.
+ *
+ * @param d value to multiply all entries by
+ * @return d * this
+ */
+ RealMatrix scalarMultiply(double d);
+
+ /**
+ * Returns the result of postmultiplying this by m.
+ *
+ * @param m matrix to postmultiply by
+ * @return this * m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ RealMatrix multiply(RealMatrix m) throws IllegalArgumentException;
+
+ /**
+ * Returns the result premultiplying this by <code>m</code>.
+ * @param m matrix to premultiply by
+ * @return m * this
+ * @throws IllegalArgumentException
+ * if rowDimension(this) != columnDimension(m)
+ */
+ RealMatrix preMultiply(RealMatrix m) throws IllegalArgumentException;
+
+ /**
+ * 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
+ * @exception MatrixIndexException if the indices are not valid
+ */
+ RealMatrix getSubMatrix(int startRow, int endRow, int startColumn, int endColumn)
+ throws MatrixIndexException;
+
+ /**
+ * 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
+ * @exception MatrixIndexException if row or column selections are not valid
+ */
+ RealMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws MatrixIndexException;
+
+ /**
+ * 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)
+ * @exception MatrixIndexException if the indices are not valid
+ * @exception IllegalArgumentException if the destination array is too small
+ */
+ void copySubMatrix(int startRow, int endRow, int startColumn, int endColumn,
+ double[][] destination)
+ throws MatrixIndexException, IllegalArgumentException;
+
+ /**
+ * 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)
+ * @exception MatrixIndexException if the indices are not valid
+ * @exception IllegalArgumentException if the destination array is too small
+ */
+ void copySubMatrix(int[] selectedRows, int[] selectedColumns, double[][] destination)
+ throws MatrixIndexException, IllegalArgumentException;
+
+ /**
+ * Replace the submatrix starting at <code>row, column</code> using data in
+ * the input <code>subMatrix</code> 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></p>
+ *
+ * @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 MatrixIndexException if subMatrix does not fit into this
+ * matrix from element in (row, column)
+ * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>subMatrix</code> is null
+ * @since 2.0
+ */
+ void setSubMatrix(double[][] subMatrix, int row, int column)
+ throws MatrixIndexException;
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row matrix
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ RealMatrix getRowMatrix(int row) throws MatrixIndexException;
+
+ /**
+ * 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 MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance row
+ */
+ void setRowMatrix(int row, RealMatrix matrix)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a column matrix. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column matrix
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ RealMatrix getColumnMatrix(int column) throws MatrixIndexException;
+
+ /**
+ * 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 MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the matrix dimensions do not match one
+ * instance column
+ */
+ void setColumnMatrix(int column, RealMatrix matrix)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in row number <code>row</code>
+ * as a vector. Row indices start at 0.
+ *
+ * @param row the row to be fetched
+ * @return row vector
+ * @throws MatrixIndexException if the specified row index is invalid
+ */
+ RealVector getRowVector(int row) throws MatrixIndexException;
+
+ /**
+ * Sets the entries in row number <code>row</code>
+ * as a vector. Row indices start at 0.
+ *
+ * @param row the row to be set
+ * @param vector row vector (must have the same number of columns
+ * as the instance)
+ * @throws MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the vector dimension does not match one
+ * instance row
+ */
+ void setRowVector(int row, RealVector vector)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>column</code>
+ * as a vector. Column indices start at 0.
+ *
+ * @param column the column to be fetched
+ * @return column vector
+ * @throws MatrixIndexException if the specified column index is invalid
+ */
+ RealVector getColumnVector(int column) throws MatrixIndexException;
+
+ /**
+ * Sets the entries in column number <code>column</code>
+ * as a vector. Column indices start at 0.
+ *
+ * @param column the column to be set
+ * @param vector column vector (must have the same number of rows as the instance)
+ * @throws MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the vector dimension does not match one
+ * instance column
+ */
+ void setColumnVector(int column, RealVector vector)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in row number <code>row</code> as an array.
+ * <p>
+ * Row indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= row < rowDimension.</code></p>
+ *
+ * @param row the row to be fetched
+ * @return array of entries in the row
+ * @throws MatrixIndexException if the specified row index is not valid
+ */
+ double[] getRow(int row) throws MatrixIndexException;
+
+ /**
+ * 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 array row matrix (must have the same number of columns as the instance)
+ * @throws MatrixIndexException if the specified row index is invalid
+ * @throws InvalidMatrixException if the array size does not match one
+ * instance row
+ */
+ void setRow(int row, double[] array)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entries in column number <code>col</code> as an array.
+ * <p>
+ * Column indices start at 0. A <code>MatrixIndexException</code> is thrown
+ * unless <code>0 <= column < columnDimension.</code></p>
+ *
+ * @param column the column to be fetched
+ * @return array of entries in the column
+ * @throws MatrixIndexException if the specified column index is not valid
+ */
+ double[] getColumn(int column) throws MatrixIndexException;
+
+ /**
+ * 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 array column array (must have the same number of rows as the instance)
+ * @throws MatrixIndexException if the specified column index is invalid
+ * @throws InvalidMatrixException if the array size does not match one
+ * instance column
+ */
+ void setColumn(int column, double[] array)
+ throws MatrixIndexException, InvalidMatrixException;
+
+ /**
+ * Returns the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ */
+ double getEntry(int row, int column) throws MatrixIndexException;
+
+ /**
+ * Set the entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void setEntry(int row, int column, double value) throws MatrixIndexException;
+
+ /**
+ * Change an entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 row,column
+ * @throws MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void addToEntry(int row, int column, double increment) throws MatrixIndexException;
+
+ /**
+ * Change an entry in the specified row and column.
+ * <p>
+ * Row and column indices start at 0 and must satisfy
+ * <ul>
+ * <li><code>0 <= row < rowDimension</code></li>
+ * <li><code> 0 <= column < columnDimension</code></li>
+ * </ul>
+ * otherwise a <code>MatrixIndexException</code> is thrown.</p>
+ *
+ * @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 row,column
+ * @throws MatrixIndexException if the row or column index is not valid
+ * @since 2.0
+ */
+ void multiplyEntry(int row, int column, double factor) throws MatrixIndexException;
+
+ /**
+ * Returns the transpose of this matrix.
+ *
+ * @return transpose matrix
+ */
+ RealMatrix transpose();
+
+ /**
+ * Returns the inverse of this matrix.
+ *
+ * @return inverse matrix
+ * @throws InvalidMatrixException if this is not invertible
+ * @deprecated as of release 2.0, replaced by <code>
+ * {@link LUDecompositionImpl#LUDecompositionImpl(RealMatrix)
+ * new LUDecompositionImpl(m)}.{@link LUDecomposition#getSolver()
+ * getSolver()}.{@link DecompositionSolver#getInverse()
+ * getInverse()}</code>
+ */
+ @Deprecated
+ RealMatrix inverse() throws InvalidMatrixException;
+
+ /**
+ * Returns the determinant of this matrix.
+ *
+ * @return determinant
+ * @deprecated as of release 2.0, replaced by <code>
+ * {@link LUDecompositionImpl#LUDecompositionImpl(RealMatrix)
+ * new LUDecompositionImpl(m)}.{@link LUDecomposition#getDeterminant()
+ * getDeterminant()}</code>
+ */
+ @Deprecated
+ double getDeterminant();
+
+ /**
+ * Is this a singular matrix?
+ * @return true if the matrix is singular
+ * @deprecated as of release 2.0, replaced by the boolean negation of
+ * <code>{@link LUDecompositionImpl#LUDecompositionImpl(RealMatrix)
+ * new LUDecompositionImpl(m)}.{@link LUDecomposition#getSolver()
+ * getSolver()}.{@link DecompositionSolver#isNonSingular()
+ * isNonSingular()}</code>
+ */
+ @Deprecated
+ boolean isSingular();
+
+ /**
+ * 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
+ */
+ double getTrace() throws NonSquareMatrixException;
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ double[] operate(double[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the result of multiplying this by the vector <code>v</code>.
+ *
+ * @param v the vector to operate on
+ * @return this*v
+ * @throws IllegalArgumentException if columnDimension != v.size()
+ */
+ RealVector operate(RealVector v) throws IllegalArgumentException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ double[] preMultiply(double[] v) throws IllegalArgumentException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
+ *
+ * @param v the row vector to premultiply by
+ * @return v*this
+ * @throws IllegalArgumentException if rowDimension != v.size()
+ */
+ RealVector preMultiply(RealVector v) throws IllegalArgumentException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @param visitor visitor used to process all matrix entries
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @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)
+ throws MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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)
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * 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.</p>
+ * @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)
+ * @exception MatrixVisitorException if the visitor cannot process an entry
+ * @exception MatrixIndexException if the indices are not valid
+ * @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 MatrixIndexException, MatrixVisitorException;
+
+ /**
+ * Returns the solution vector for a linear system with coefficient
+ * matrix = this and constant vector = <code>b</code>.
+ *
+ * @param b constant vector
+ * @return vector of solution values to AX = b, where A is *this
+ * @throws IllegalArgumentException if this.rowDimension != b.length
+ * @throws InvalidMatrixException if this matrix is not square or is singular
+ * @deprecated as of release 2.0, replaced by {@link DecompositionSolver#solve(double[])}
+ */
+ @Deprecated
+ double[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException;
+
+ /**
+ * Returns a matrix of (column) solution vectors for linear systems with
+ * coefficient matrix = this and constant vectors = columns of
+ * <code>b</code>.
+ *
+ * @param b matrix of constant vectors forming RHS of linear systems to
+ * to solve
+ * @return matrix of solution vectors
+ * @throws IllegalArgumentException if this.rowDimension != row dimension
+ * @throws InvalidMatrixException if this matrix is not square or is singular
+ * @deprecated as of release 2.0, replaced by {@link DecompositionSolver#solve(RealMatrix)}
+ */
+ @Deprecated
+ RealMatrix solve(RealMatrix b) throws IllegalArgumentException, InvalidMatrixException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math/linear/RealMatrixChangingVisitor.java
new file mode 100644
index 0000000..25e8f96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealMatrixChangingVisitor.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.math.linear;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @see DefaultRealMatrixChangingVisitor
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public interface RealMatrixChangingVisitor {
+
+ /**
+ * Start visiting a matrix.
+ * <p>This method is called once before any entry of the matrix is visited.</p>
+ * @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
+ * @throws MatrixVisitorException if something wrong occurs
+ */
+ double visit(int row, int column, double value)
+ throws MatrixVisitorException;
+
+ /**
+ * End visiting a matrix.
+ * <p>This method is called once after all entries of the matrix have been visited.</p>
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ double end();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealMatrixImpl.java b/src/main/java/org/apache/commons/math/linear/RealMatrixImpl.java
new file mode 100644
index 0000000..7dbfdba
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealMatrixImpl.java
@@ -0,0 +1,629 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Implementation of RealMatrix using a double[][] array to store entries and
+ * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
+ * LU decomposition</a> to support linear system
+ * solution and inverse.
+ * <p>
+ * The LU decomposition is performed as needed, to support the following operations: <ul>
+ * <li>solve</li>
+ * <li>isSingular</li>
+ * <li>getDeterminant</li>
+ * <li>inverse</li> </ul></p>
+ * <p>
+ * <strong>Usage notes</strong>:<br>
+ * <ul><li>
+ * The LU decomposition is cached and reused on subsequent calls.
+ * If data are modified via references to the underlying array obtained using
+ * <code>getDataRef()</code>, then the stored LU decomposition will not be
+ * discarded. In this case, you need to explicitly invoke
+ * <code>LUDecompose()</code> to recompute the decomposition
+ * before using any of the methods above.</li>
+ * <li>
+ * As specified in the {@link RealMatrix} 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.</li></ul>
+ * </p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @deprecated as of 2.0 replaced by {@link Array2DRowRealMatrix}
+ */
+@Deprecated
+public class RealMatrixImpl extends AbstractRealMatrix implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1067294169172445528L;
+
+ /** Entries of the matrix */
+ protected double data[][];
+
+ /**
+ * Creates a matrix with no data
+ */
+ public RealMatrixImpl() {
+ }
+
+ /**
+ * 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 IllegalArgumentException if row or column dimension is not
+ * positive
+ */
+ public RealMatrixImpl(final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ super(rowDimension, columnDimension);
+ data = new double[rowDimension][columnDimension];
+ }
+
+ /**
+ * Create a new 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 #RealMatrixImpl(double[][], boolean)}
+ * with the second argument set to <code>true</code>.</p>
+ *
+ * @param d data for new matrix
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #RealMatrixImpl(double[][], boolean)
+ */
+ public RealMatrixImpl(final double[][] d)
+ throws IllegalArgumentException, NullPointerException {
+ copyIn(d);
+ }
+
+ /**
+ * Create a new RealMatrix using the input array as the underlying
+ * data array.
+ * <p>If an array is built specially in order to be embedded in a
+ * RealMatrix and not used directly, the <code>copyArray</code> may be
+ * set to <code>false</code. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.</p>
+ * @param d data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ * @throws IllegalArgumentException if <code>d</code> is not rectangular
+ * (not all rows have the same length) or empty
+ * @throws NullPointerException if <code>d</code> is null
+ * @see #RealMatrixImpl(double[][])
+ */
+ public RealMatrixImpl(final double[][] d, final boolean copyArray)
+ throws IllegalArgumentException, NullPointerException {
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ if (d == null) {
+ throw new NullPointerException();
+ }
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, d[r].length);
+ }
+ }
+ data = d;
+ }
+ }
+
+ /**
+ * Create a new (column) RealMatrix using <code>v</code> as the
+ * data for the unique column of the <code>v.length x 1</code> matrix
+ * created.
+ * <p>The input array is copied, not referenced.</p>
+ *
+ * @param v column vector holding data for new matrix
+ */
+ public RealMatrixImpl(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 IllegalArgumentException {
+ return new RealMatrixImpl(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix copy() {
+ return new RealMatrixImpl(copyOut(), false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix add(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return add((RealMatrixImpl) m);
+ } catch (ClassCastException cce) {
+ return super.add(m);
+ }
+ }
+
+ /**
+ * Compute the sum of this and <code>m</code>.
+ *
+ * @param m matrix to be added
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public RealMatrixImpl add(final RealMatrixImpl m)
+ throws IllegalArgumentException {
+
+ // 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 RealMatrixImpl(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix subtract(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return subtract((RealMatrixImpl) m);
+ } catch (ClassCastException cce) {
+ return super.subtract(m);
+ }
+ }
+
+ /**
+ * Compute this minus <code>m</code>.
+ *
+ * @param m matrix to be subtracted
+ * @return this + m
+ * @throws IllegalArgumentException if m is not the same size as this
+ */
+ public RealMatrixImpl subtract(final RealMatrixImpl m)
+ throws IllegalArgumentException {
+
+ // safety check
+ 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 RealMatrixImpl(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix multiply(final RealMatrix m)
+ throws IllegalArgumentException {
+ try {
+ return multiply((RealMatrixImpl) m);
+ } catch (ClassCastException cce) {
+ return super.multiply(m);
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by <code>m</code>.
+ * @param m matrix to postmultiply by
+ * @return this*m
+ * @throws IllegalArgumentException
+ * if columnDimension(this) != rowDimension(m)
+ */
+ public RealMatrixImpl multiply(final RealMatrixImpl m)
+ throws IllegalArgumentException {
+
+ // safety check
+ 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];
+ for (int row = 0; row < nRows; row++) {
+ final double[] dataRow = data[row];
+ final double[] outDataRow = outData[row];
+ for (int col = 0; col < nCols; col++) {
+ double sum = 0;
+ for (int i = 0; i < nSum; i++) {
+ sum += dataRow[i] * m.data[i][col];
+ }
+ outDataRow[col] = sum;
+ }
+ }
+
+ return new RealMatrixImpl(outData, false);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ * <p>
+ * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
+ *
+ * @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 MatrixIndexException {
+ if (data == null) {
+ if (row > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET,
+ row);
+ }
+ if (column > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET,
+ column);
+ }
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ nCols, subMatrix[i].length);
+ }
+ 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 MatrixIndexException {
+ try {
+ return data[row][column];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws MatrixIndexException {
+ try {
+ data[row][column] = value;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws MatrixIndexException {
+ try {
+ data[row][column] += increment;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws MatrixIndexException {
+ try {
+ data[row][column] *= factor;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MatrixIndexException(
+ LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
+ row, column, getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /** {@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 IllegalArgumentException {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (v.length != nCols) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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 IllegalArgumentException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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)
+ throws MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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 MatrixIndexException, MatrixVisitorException {
+ 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();
+ }
+
+ /**
+ * Returns 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;
+ }
+
+ /**
+ * Replaces data with a fresh copy of the input array.
+ * <p>
+ * Verifies that the input array is rectangular and non-empty.</p>
+ *
+ * @param in data to copy in
+ * @throws IllegalArgumentException if input array is empty or not
+ * rectangular
+ * @throws NullPointerException if input array is null
+ */
+ private void copyIn(final double[][] in) {
+ setSubMatrix(in, 0, 0);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math/linear/RealMatrixPreservingVisitor.java
new file mode 100644
index 0000000..2186b79
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealMatrixPreservingVisitor.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.math.linear;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @see DefaultRealMatrixPreservingVisitor
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public interface RealMatrixPreservingVisitor {
+
+ /**
+ * Start visiting a matrix.
+ * <p>This method is called once before any entry of the matrix is visited.</p>
+ * @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
+ * @throws MatrixVisitorException if something wrong occurs
+ */
+ void visit(int row, int column, double value)
+ throws MatrixVisitorException;
+
+ /**
+ * End visiting a matrix.
+ * <p>This method is called once after all entries of the matrix have been visited.</p>
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ double end();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealVector.java b/src/main/java/org/apache/commons/math/linear/RealVector.java
new file mode 100644
index 0000000..0d7982b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealVector.java
@@ -0,0 +1,1005 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.util.Iterator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+
+/**
+ * Interface defining a real-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>
+ * <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:
+ * </p>
+ * <pre>
+ * RealVector result = v.mapAddToSelf(3.0).mapTanToSelf().mapSquareToSelf();
+ * </pre>
+ * <p>
+ * Remark on the deprecated {@code mapXxx} and {@code mapXxxToSelf} methods: In
+ * Commons-Math v3.0, the same functionality will be achieved by directly using the
+ * {@link #map(UnivariateRealFunction)} and {@link #mapToSelf(UnivariateRealFunction)}
+ * together with new function objects (not available in v2.2).
+ * </p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public interface RealVector {
+ /**
+ * 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>
+ *
+ * @param function Function to apply to each entry.
+ * @return this vector.
+ * @throws FunctionEvaluationException if the function throws it.
+ */
+ RealVector mapToSelf(UnivariateRealFunction function) throws FunctionEvaluationException;
+
+ /**
+ * Acts as if implemented as:
+ * <pre>
+ * return copy().map(function);
+ * </pre>
+ *
+ * @param function Function to apply to each entry.
+ * @return a new vector.
+ * @throws FunctionEvaluationException if the function throws it.
+ */
+ RealVector map(UnivariateRealFunction function) throws FunctionEvaluationException;
+
+ /** Class representing a modifiable entry in the vector. */
+ public abstract class Entry {
+ /** Index of the entry. */
+ private int index;
+
+ /**
+ * Get the value of the entry.
+ *
+ * @return the value of the entry.
+ */
+ public abstract double getValue();
+ /**
+ * Set the value of the entry.
+ *
+ * @param value New value for the entry.
+ */
+ public abstract void setValue(double 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;
+ }
+ }
+
+ /**
+ * Generic dense iterator.
+ * It iterates in increasing order of the vector index.
+ *
+ * @return a dense iterator
+ */
+ Iterator<Entry> iterator();
+
+ /**
+ * Specialized implementations may choose to not iterate over all
+ * dimensions, either because those values are unset, or are equal
+ * to defaultValue(), or are small enough to be ignored for the
+ * purposes of iteration.
+ * No guarantees are made about order of iteration.
+ * In dense implementations, this method will often delegate to
+ * {@link #iterator()}.
+ *
+ * @return a sparse iterator
+ */
+ Iterator<Entry> sparseIterator();
+
+ /**
+ * Returns a (deep) copy of this vector.
+ *
+ * @return a vector copy.
+ */
+ RealVector copy();
+
+ /**
+ * Compute the sum of this vector and {@code v}.
+ *
+ * @param v Vector to be added.
+ * @return {@code this} + {@code v}.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector add(RealVector v);
+
+ /**
+ * Compute the sum of this vector and {@code v}.
+ *
+ * @param v Vector to be added.
+ * @return {@code this} + {@code v}.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector add(double[] v);
+
+
+ /**
+ * Subtract {@code v} from this vector.
+ *
+ * @param v Vector to be subtracted.
+ * @return {@code this} - {@code v}.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector subtract(RealVector v);
+
+ /**
+ * Subtract {@code v} from this vector.
+ *
+ * @param v Vector to be subtracted.
+ * @return {@code this} - {@code v}.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector subtract(double[] v);
+
+ /**
+ * Add a value to each entry.
+ *
+ * @param d Value to be added to each entry.
+ * @return {@code this} + {@code d}.
+ */
+ RealVector mapAdd(double 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}.
+ */
+ RealVector mapAddToSelf(double d);
+
+ /**
+ * Subtract a value from each entry.
+ *
+ * @param d Value to be subtracted.
+ * @return {@code this} - {@code d}.
+ */
+ RealVector mapSubtract(double d);
+
+ /**
+ * Subtract a value from each entry.
+ * The instance is changed in-place.
+ *
+ * @param d Value to be subtracted.
+ * @return {@code this}.
+ */
+ RealVector mapSubtractToSelf(double d);
+
+ /**
+ * Multiply each entry.
+ *
+ * @param d Multiplication factor.
+ * @return {@code this} * {@code d}.
+ */
+ RealVector mapMultiply(double d);
+
+ /**
+ * Multiply each entry.
+ * The instance is changed in-place.
+ *
+ * @param d Multiplication factor.
+ * @return {@code this}.
+ */
+ RealVector mapMultiplyToSelf(double d);
+
+ /**
+ * Divide each entry.
+ *
+ * @param d Value to divide by.
+ * @return {@code this} / {@code d}.
+ */
+ RealVector mapDivide(double d);
+
+ /**
+ * Divide each entry.
+ * The instance is changed in-place.
+ *
+ * @param d Value to divide by.
+ * @return {@code this}.
+ */
+ RealVector mapDivideToSelf(double d);
+
+ /**
+ * Map a power operation to each entry.
+ *
+ * @param d Operator value.
+ * @return a mapped copy of the vector.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapPow(double d);
+
+ /**
+ * Map a power operation to each entry.
+ * The instance is changed in-place.
+ *
+ * @param d Operator value.
+ * @return the mapped vector.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapPowToSelf(double d);
+
+ /**
+ * Map the {@link Math#exp(double)} function to each entry.
+ *
+ * @return a mapped copy of the vector.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapExp();
+
+ /**
+ * Map {@link Math#exp(double)} operation to each entry.
+ * The instance is changed in-place.
+ *
+ * @return the mapped vector.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapExpToSelf();
+
+ /**
+ * Map the {@link Math#expm1(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapExpm1();
+
+ /**
+ * Map the {@link Math#expm1(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapExpm1ToSelf();
+
+ /**
+ * Map the {@link Math#log(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLog();
+
+ /**
+ * Map the {@link Math#log(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLogToSelf();
+
+ /**
+ * Map the {@link Math#log10(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLog10();
+
+ /**
+ * Map the {@link Math#log10(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLog10ToSelf();
+
+ /**
+ * Map the {@link Math#log1p(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLog1p();
+
+ /**
+ * Map the {@link Math#log1p(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapLog1pToSelf();
+
+ /**
+ * Map the {@link Math#cosh(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCosh();
+
+ /**
+ * Map the {@link Math#cosh(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCoshToSelf();
+
+ /**
+ * Map the {@link Math#sinh(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSinh();
+
+ /**
+ * Map the {@link Math#sinh(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSinhToSelf();
+
+ /**
+ * Map the {@link Math#tanh(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapTanh();
+
+ /**
+ * Map the {@link Math#tanh(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapTanhToSelf();
+
+ /**
+ * Map the {@link Math#cos(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCos();
+
+ /**
+ * Map the {@link Math#cos(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCosToSelf();
+
+ /**
+ * Map the {@link Math#sin(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSin();
+
+ /**
+ * Map the {@link Math#sin(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSinToSelf();
+
+ /**
+ * Map the {@link Math#tan(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapTan();
+
+ /**
+ * Map the {@link Math#tan(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapTanToSelf();
+
+ /**
+ * Map the {@link Math#acos(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAcos();
+
+ /**
+ * Map the {@link Math#acos(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAcosToSelf();
+
+ /**
+ * Map the {@link Math#asin(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAsin();
+
+ /**
+ * Map the {@link Math#asin(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAsinToSelf();
+
+ /**
+ * Map the {@link Math#atan(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAtan();
+
+ /**
+ * Map the {@link Math#atan(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAtanToSelf();
+
+ /**
+ * Map the 1/x function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapInv();
+
+ /**
+ * Map the 1/x function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapInvToSelf();
+
+ /**
+ * Map the {@link Math#abs(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAbs();
+
+ /**
+ * Map the {@link Math#abs(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapAbsToSelf();
+
+ /**
+ * Map the {@link Math#sqrt(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSqrt();
+
+ /**
+ * Map the {@link Math#sqrt(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSqrtToSelf();
+
+ /**
+ * Map the {@link Math#cbrt(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCbrt();
+
+ /**
+ * Map the {@link Math#cbrt(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCbrtToSelf();
+
+ /**
+ * Map the {@link Math#ceil(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCeil();
+
+ /**
+ * Map the {@link Math#ceil(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapCeilToSelf();
+
+ /**
+ * Map the {@link Math#floor(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapFloor();
+
+ /**
+ * Map the {@link Math#floor(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapFloorToSelf();
+
+ /**
+ * Map the {@link Math#rint(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapRint();
+
+ /**
+ * Map the {@link Math#rint(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapRintToSelf();
+
+ /**
+ * Map the {@link Math#signum(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSignum();
+
+ /**
+ * Map the {@link Math#signum(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapSignumToSelf();
+
+ /**
+ * Map the {@link Math#ulp(double)} function to each entry.
+ * @return a vector containing the result of applying the function to each entry
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapUlp();
+
+ /**
+ * Map the {@link Math#ulp(double)} function to each entry.
+ * <p>The instance <strong>is</strong> changed by this method.</p>
+ * @return for convenience, return this
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ RealVector mapUlpToSelf();
+
+ /**
+ * 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 org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector ebeMultiply(RealVector v);
+
+ /**
+ * 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 org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector ebeMultiply(double[] v);
+
+ /**
+ * 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 org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector ebeDivide(RealVector v);
+
+ /**
+ * 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 org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector ebeDivide(double[] v);
+
+ /**
+ * Returns vector entries as a double array.
+ * @return double array of entries
+ */
+ double[] getData();
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ double dotProduct(RealVector v);
+
+ /**
+ * Compute the dot product.
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product between instance and v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ double dotProduct(double[] v);
+
+ /**
+ * 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.</p>
+ * @return norm
+ * @see #getL1Norm()
+ * @see #getLInfNorm()
+ * @see #getDistance(RealVector)
+ */
+ double getNorm();
+
+ /**
+ * 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 elements.</p>
+ * @return norm
+ * @see #getNorm()
+ * @see #getLInfNorm()
+ * @see #getL1Distance(RealVector)
+ */
+ double getL1Norm();
+
+ /**
+ * 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 elements.</p>
+ * @return norm
+ * @see #getNorm()
+ * @see #getL1Norm()
+ * @see #getLInfDistance(RealVector)
+ */
+ double getLInfNorm();
+
+ /**
+ * 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
+ * elements differences, or euclidian distance.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getL1Distance(RealVector)
+ * @see #getLInfDistance(RealVector)
+ * @see #getNorm()
+ */
+ double getDistance(RealVector v);
+
+ /**
+ * 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
+ * elements differences, or euclidian distance.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getL1Distance(double[])
+ * @see #getLInfDistance(double[])
+ * @see #getNorm()
+ */
+ double getDistance(double[] v);
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getDistance(RealVector)
+ * @see #getLInfDistance(RealVector)
+ * @see #getL1Norm()
+ */
+ double getL1Distance(RealVector v);
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getDistance(double[])
+ * @see #getLInfDistance(double[])
+ * @see #getL1Norm()
+ */
+ double getL1Distance(double[] v);
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getDistance(RealVector)
+ * @see #getL1Distance(RealVector)
+ * @see #getLInfNorm()
+ */
+ double getLInfDistance(RealVector v);
+
+ /**
+ * 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
+ * elements differences.</p>
+ * @param v vector to which distance is requested
+ * @return distance between two vectors.
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ * @see #getDistance(double[])
+ * @see #getL1Distance(double[])
+ * @see #getLInfNorm()
+ */
+ double getLInfDistance(double[] v);
+
+ /** Creates a unit vector pointing in the direction of this vector.
+ * <p>The instance is not changed by this method.</p>
+ * @return a unit vector pointing in direction of this vector
+ * @exception ArithmeticException if the norm is null
+ */
+ RealVector unitVector();
+
+ /** Converts this vector into a unit vector.
+ * <p>The instance itself is changed by this method.</p>
+ * @throws ArithmeticException
+ * if the norm is zero.
+ */
+ void unitize();
+
+ /** 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 v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector projection(RealVector v);
+
+ /** 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 v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealVector projection(double[] v);
+
+ /**
+ * Compute the outer product.
+ * @param v vector with which outer product should be computed
+ * @return the square matrix outer product between instance and v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealMatrix outerProduct(RealVector v);
+
+ /**
+ * Compute the outer product.
+ * @param v vector with which outer product should be computed
+ * @return the square matrix outer product between instance and v
+ * @throws org.apache.commons.math.exception.DimensionMismatchException
+ * if {@code v} is not the same size as this vector.
+ */
+ RealMatrix outerProduct(double[] 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 org.apache.commons.math.exception.OutOfRangeException
+ * if the index is not valid.
+ * @see #setEntry(int, double)
+ */
+ double getEntry(int index);
+
+ /**
+ * Set a single element.
+ * @param index element index.
+ * @param value new value for the element.
+ * @throws org.apache.commons.math.exception.OutOfRangeException
+ * if the index is not valid.
+ * @see #getEntry(int)
+ */
+ void setEntry(int index, double value);
+
+ /**
+ * 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
+ */
+ RealVector append(RealVector v);
+
+ /**
+ * Construct a vector by appending a double to this vector.
+ * @param d double to append.
+ * @return a new vector
+ */
+ RealVector append(double d);
+
+ /**
+ * Construct a vector by appending a double array to this vector.
+ * @param a double array to append.
+ * @return a new vector
+ */
+ RealVector append(double[] a);
+
+ /**
+ * 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 org.apache.commons.math.exception.OutOfRangeException
+ * if the index is not valid.
+ */
+ RealVector getSubVector(int index, int n);
+
+ /**
+ * Set a set of consecutive elements.
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @throws org.apache.commons.math.exception.OutOfRangeException
+ * if the index is not valid.
+ * @see #setSubVector(int, double[])
+ */
+ void setSubVector(int index, RealVector v);
+
+ /**
+ * Set a set of consecutive elements.
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @throws org.apache.commons.math.exception.OutOfRangeException
+ * if the index is not valid.
+ * @see #setSubVector(int, RealVector)
+ */
+ void setSubVector(int index, double[] v);
+
+ /**
+ * Set all elements to a single value.
+ * @param value single value to set for all elements
+ */
+ void set(double value);
+
+ /**
+ * Convert the vector to a double array.
+ * <p>The array is independent from vector data, it's elements
+ * are copied.</p>
+ * @return array containing a copy of vector elements
+ */
+ double[] toArray();
+
+ /**
+ * 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.
+ */
+ 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.
+ */
+ boolean isInfinite();
+}
diff --git a/src/main/java/org/apache/commons/math/linear/RealVectorFormat.java b/src/main/java/org/apache/commons/math/linear/RealVectorFormat.java
new file mode 100644
index 0000000..ecefba2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/RealVectorFormat.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.math.linear;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.CompositeFormat;
+
+/**
+ * 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>
+ * <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>
+ *
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ * @since 2.0
+ */
+public class RealVectorFormat extends CompositeFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -708767813036157690L;
+
+ /** 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.</p>
+ */
+ public RealVectorFormat() {
+ this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, 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, 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.</p>
+ * @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(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * This static method calls {@link #format(Object)} on a default instance of
+ * RealVectorFormat.
+ *
+ * @param v RealVector object to format
+ * @return A formatted vector
+ */
+ public static String formatRealVector(RealVector v) {
+ return getInstance().format(v);
+ }
+
+ /**
+ * 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);
+ }
+ formatDouble(vector.getEntry(i), format, toAppendTo, pos);
+ }
+
+ // format suffix
+ toAppendTo.append(suffix);
+
+ return toAppendTo;
+
+ }
+
+ /**
+ * Formats a object to produce a string.
+ * <p><code>obj</code> must be a {@link RealVector} object. Any other type of
+ * object will result in an {@link IllegalArgumentException} being thrown.</p>
+ * @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 IllegalArgumentException is <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo,
+ FieldPosition pos) {
+
+ if (obj instanceof RealVector) {
+ return format( (RealVector)obj, toAppendTo, pos);
+ }
+
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_REAL_VECTOR,
+ obj.getClass().getName());
+
+ }
+
+ /**
+ * Parses a string to produce a {@link RealVector} object.
+ * @param source the string to parse
+ * @return the parsed {@link RealVector} object.
+ * @exception ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ public ArrayRealVector parse(String source) throws ParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ ArrayRealVector result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw MathRuntimeException.createParseException(
+ parsePosition.getErrorIndex(),
+ LocalizedFormats.UNPARSEABLE_REAL_VECTOR, source);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link RealVector} object.
+ * @param source the 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
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedPrefix, pos)) {
+ return null;
+ }
+
+ // parse components
+ List<Number> components = new ArrayList<Number>();
+ for (boolean loop = true; loop;){
+
+ if (!components.isEmpty()) {
+ parseAndIgnoreWhitespace(source, pos);
+ if (!parseFixedstring(source, trimmedSeparator, pos)) {
+ loop = false;
+ }
+ }
+
+ if (loop) {
+ parseAndIgnoreWhitespace(source, pos);
+ Number component = 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
+ parseAndIgnoreWhitespace(source, pos);
+ if (!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);
+
+ }
+
+ /**
+ * Parses a string to produce a object.
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed object.
+ * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
+ */
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return parse(source, pos);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/SingularMatrixException.java b/src/main/java/org/apache/commons/math/linear/SingularMatrixException.java
new file mode 100644
index 0000000..a14cf69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SingularMatrixException.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.math.linear;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+
+/**
+ * Thrown when a matrix is singular.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class SingularMatrixException extends InvalidMatrixException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7379143356784298432L;
+
+ /**
+ * Construct an exception with a default message.
+ */
+ public SingularMatrixException() {
+ super(LocalizedFormats.SINGULAR_MATRIX);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java b/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java
new file mode 100644
index 0000000..5b45cde
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+
+
+/**
+ * An interface to classes that implement an algorithm to calculate the
+ * Singular Value Decomposition of a real 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>
+ * <p>This interface 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:</p>
+ * <ul>
+ * <li>the <code>norm2</code> method which has been renamed as {@link #getNorm()
+ * getNorm},</li>
+ * <li>the <code>cond</code> method which has been renamed as {@link
+ * #getConditionNumber() getConditionNumber},</li>
+ * <li>the <code>rank</code> method which has been renamed as {@link #getRank()
+ * getRank},</li>
+ * <li>a {@link #getUT() getUT} method has been added,</li>
+ * <li>a {@link #getVT() getVT} method has been added,</li>
+ * <li>a {@link #getSolver() getSolver} method has been added,</li>
+ * <li>a {@link #getCovariance(double) getCovariance} method has been added.</li>
+ * </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>
+ * @version $Revision: 928081 $ $Date: 2010-03-26 23:36:38 +0100 (ven. 26 mars 2010) $
+ * @since 2.0
+ */
+public interface SingularValueDecomposition {
+
+ /**
+ * Returns the matrix U of the decomposition.
+ * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * @return the U matrix
+ * @see #getUT()
+ */
+ RealMatrix getU();
+
+ /**
+ * Returns the transpose of the matrix U of the decomposition.
+ * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * @return the U matrix (or null if decomposed matrix is singular)
+ * @see #getU()
+ */
+ RealMatrix getUT();
+
+ /**
+ * 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.</p>
+ * @return the &Sigma; matrix
+ */
+ RealMatrix getS();
+
+ /**
+ * 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.</p>
+ * @return the diagonal elements of the &Sigma; matrix
+ */
+ double[] getSingularValues();
+
+ /**
+ * Returns the matrix V of the decomposition.
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * @return the V matrix (or null if decomposed matrix is singular)
+ * @see #getVT()
+ */
+ RealMatrix getV();
+
+ /**
+ * Returns the transpose of the matrix V of the decomposition.
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
+ * @return the V matrix (or null if decomposed matrix is singular)
+ * @see #getV()
+ */
+ RealMatrix getVT();
+
+ /**
+ * 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.</p>
+ * @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
+ */
+ RealMatrix getCovariance(double minSingularValue) throws IllegalArgumentException;
+
+ /**
+ * 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).</p>
+ * @return norm
+ */
+ double getNorm();
+
+ /**
+ * Return the condition number of the matrix.
+ * @return condition number of the matrix
+ */
+ double getConditionNumber();
+
+ /**
+ * 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.</p>
+ * @return effective numerical matrix rank
+ */
+ int getRank();
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ * @return a solver
+ */
+ DecompositionSolver getSolver();
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java b/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java
new file mode 100644
index 0000000..d776b66
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class SingularValueDecompositionImpl implements
+ SingularValueDecomposition {
+
+ /** Number of rows of the initial matrix. */
+ private int m;
+
+ /** Number of columns of the initial matrix. */
+ private int n;
+
+ /** Eigen decomposition of the tridiagonal matrix. */
+ private EigenDecomposition eigenDecomposition;
+
+ /** Singular values. */
+ private double[] singularValues;
+
+ /** Cached value of U. */
+ private RealMatrix cachedU;
+
+ /** Cached value of U<sup>T</sup>. */
+ private RealMatrix cachedUt;
+
+ /** Cached value of S. */
+ private RealMatrix cachedS;
+
+ /** Cached value of V. */
+ private RealMatrix cachedV;
+
+ /** Cached value of V<sup>T</sup>. */
+ private RealMatrix cachedVt;
+
+ /**
+ * Calculates the compact Singular Value Decomposition of the given matrix.
+ * @param matrix
+ * The matrix to decompose.
+ * @exception InvalidMatrixException
+ * (wrapping a
+ * {@link org.apache.commons.math.ConvergenceException} if
+ * algorithm fails to converge
+ */
+ public SingularValueDecompositionImpl(final RealMatrix matrix)
+ throws InvalidMatrixException {
+
+ m = matrix.getRowDimension();
+ n = matrix.getColumnDimension();
+
+ cachedU = null;
+ cachedS = null;
+ cachedV = null;
+ cachedVt = null;
+
+ double[][] localcopy = matrix.getData();
+ double[][] matATA = new double[n][n];
+ //
+ // create A^T*A
+ //
+ for (int i = 0; i < n; i++) {
+ for (int j = i; j < n; j++) {
+ matATA[i][j] = 0.0;
+ for (int k = 0; k < m; k++) {
+ matATA[i][j] += localcopy[k][i] * localcopy[k][j];
+ }
+ matATA[j][i]=matATA[i][j];
+ }
+ }
+
+ double[][] matAAT = new double[m][m];
+ //
+ // create A*A^T
+ //
+ for (int i = 0; i < m; i++) {
+ for (int j = i; j < m; j++) {
+ matAAT[i][j] = 0.0;
+ for (int k = 0; k < n; k++) {
+ matAAT[i][j] += localcopy[i][k] * localcopy[j][k];
+ }
+ matAAT[j][i]=matAAT[i][j];
+ }
+ }
+ int p;
+ if (m>=n) {
+ p=n;
+ // compute eigen decomposition of A^T*A
+ eigenDecomposition = new EigenDecompositionImpl(
+ new Array2DRowRealMatrix(matATA),1.0);
+ singularValues = eigenDecomposition.getRealEigenvalues();
+ cachedV = eigenDecomposition.getV();
+ // compute eigen decomposition of A*A^T
+ eigenDecomposition = new EigenDecompositionImpl(
+ new Array2DRowRealMatrix(matAAT),1.0);
+ cachedU = eigenDecomposition.getV().getSubMatrix(0, m - 1, 0, p - 1);
+ } else {
+ p=m;
+ // compute eigen decomposition of A*A^T
+ eigenDecomposition = new EigenDecompositionImpl(
+ new Array2DRowRealMatrix(matAAT),1.0);
+ singularValues = eigenDecomposition.getRealEigenvalues();
+ cachedU = eigenDecomposition.getV();
+
+ // compute eigen decomposition of A^T*A
+ eigenDecomposition = new EigenDecompositionImpl(
+ new Array2DRowRealMatrix(matATA),1.0);
+ cachedV = eigenDecomposition.getV().getSubMatrix(0,n-1,0,p-1);
+ }
+ for (int i = 0; i < p; i++) {
+ singularValues[i] = FastMath.sqrt(FastMath.abs(singularValues[i]));
+ }
+ // Up to this point, U and V are computed independently of each other.
+ // There still a sign indetermination of each column of, say, U.
+ // The sign is set such that A.V_i=sigma_i.U_i (i<=p)
+ // The right sign corresponds to a positive dot product of A.V_i and U_i
+ for (int i = 0; i < p; i++) {
+ RealVector tmp = cachedU.getColumnVector(i);
+ double product=matrix.operate(cachedV.getColumnVector(i)).dotProduct(tmp);
+ if (product<0) {
+ cachedU.setColumnVector(i, tmp.mapMultiply(-1.0));
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getU() throws InvalidMatrixException {
+ // return the cached matrix
+ return cachedU;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getUT() throws InvalidMatrixException {
+
+ if (cachedUt == null) {
+ cachedUt = getU().transpose();
+ }
+
+ // return the cached matrix
+ return cachedUt;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getS() throws InvalidMatrixException {
+
+ if (cachedS == null) {
+
+ // cache the matrix for subsequent calls
+ cachedS = MatrixUtils.createRealDiagonalMatrix(singularValues);
+
+ }
+ return cachedS;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getSingularValues() throws InvalidMatrixException {
+ return singularValues.clone();
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getV() throws InvalidMatrixException {
+ // return the cached matrix
+ return cachedV;
+
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getVT() throws InvalidMatrixException {
+
+ if (cachedVt == null) {
+ cachedVt = getV().transpose();
+ }
+
+ // return the cached matrix
+ return cachedVt;
+
+ }
+
+ /** {@inheritDoc} */
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.TOO_LARGE_CUTOFF_SINGULAR_VALUE,
+ minSingularValue, singularValues[0]);
+ }
+
+ 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);
+
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() throws InvalidMatrixException {
+ return singularValues[0];
+ }
+
+ /** {@inheritDoc} */
+ public double getConditionNumber() throws InvalidMatrixException {
+ return singularValues[0] / singularValues[singularValues.length - 1];
+ }
+
+ /** {@inheritDoc} */
+ public int getRank() throws IllegalStateException {
+
+ final double threshold = FastMath.max(m, n) * FastMath.ulp(singularValues[0]);
+
+ for (int i = singularValues.length - 1; i >= 0; --i) {
+ if (singularValues[i] > threshold) {
+ return i + 1;
+ }
+ }
+ return 0;
+
+ }
+
+ /** {@inheritDoc} */
+ public DecompositionSolver getSolver() {
+ return new Solver(singularValues, getUT(), getV(), getRank() == Math
+ .max(m, n));
+ }
+
+ /** 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
+ * singularValues
+ * @param uT
+ * U<sup>T</sup> matrix of the decomposition
+ * @param v
+ * V matrix of the decomposition
+ * @param nonSingular
+ * singularity indicator
+ */
+ private Solver(final double[] singularValues, final RealMatrix uT,
+ final RealMatrix v, final boolean nonSingular) {
+ double[][] suT = uT.getData();
+ for (int i = 0; i < singularValues.length; ++i) {
+ final double a;
+ if (singularValues[i]>0) {
+ a=1.0 / singularValues[i];
+ } else {
+ a=0.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.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ */
+ public double[] solve(final double[] b) throws IllegalArgumentException {
+ 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.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ */
+ public RealVector solve(final RealVector b)
+ throws IllegalArgumentException {
+ 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.
+ * </p>
+ * @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
+ * @exception IllegalArgumentException
+ * if matrices dimensions don't match
+ */
+ public RealMatrix solve(final RealMatrix b)
+ throws IllegalArgumentException {
+ return pseudoInverse.multiply(b);
+ }
+
+ /**
+ * Check if the decomposed matrix is non-singular.
+ * @return true if the decomposed matrix is non-singular
+ */
+ public boolean isNonSingular() {
+ return nonSingular;
+ }
+
+ /**
+ * Get the pseudo-inverse of the decomposed matrix.
+ * @return inverse matrix
+ */
+ public RealMatrix getInverse() {
+ return pseudoInverse;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/SparseFieldMatrix.java b/src/main/java/org/apache/commons/math/linear/SparseFieldMatrix.java
new file mode 100644
index 0000000..1fbfb5c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SparseFieldMatrix.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.math.linear;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.util.OpenIntToFieldHashMap;
+
+/**
+ * Sparse matrix implementation based on an open addressed map.
+ *
+ * @param <T> the type of the field elements
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public class SparseFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> {
+ /**
+ * Serial id
+ */
+ private static final long serialVersionUID = 9078068119297757342L;
+ /** Storage for (sparse) matrix elements. */
+ private final OpenIntToFieldHashMap<T> entries;
+ /**
+ * row dimension
+ */
+ private final int rows;
+ /**
+ * column dimension
+ */
+ private final int columns;
+
+
+ /**
+ * Creates 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 the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @throws IllegalArgumentException if row or column dimension is not positive
+ */
+ public SparseFieldMatrix(final Field<T> field,
+ final int rowDimension, final int columnDimension)
+ throws IllegalArgumentException {
+ super(field, rowDimension, columnDimension);
+ this.rows = rowDimension;
+ this.columns = columnDimension;
+ entries = new OpenIntToFieldHashMap<T>(field);
+ }
+
+ /**
+ * Copy constructor.
+ * @param other The 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 The 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)
+ throws MatrixIndexException {
+ 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)
+ throws IllegalArgumentException {
+ return new SparseFieldMatrix<T>(getField(), rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T getEntry(int row, int column) throws MatrixIndexException {
+ 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)
+ throws MatrixIndexException {
+ 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)
+ throws MatrixIndexException {
+ 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 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/math/linear/SparseFieldVector.java b/src/main/java/org/apache/commons/math/linear/SparseFieldVector.java
new file mode 100644
index 0000000..7f4848c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SparseFieldVector.java
@@ -0,0 +1,657 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.OpenIntToFieldHashMap;
+
+/**
+ * This class implements the {@link FieldVector} interface with a {@link OpenIntToFieldHashMap} backing store.
+ * @param <T> the type of the field elements
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class SparseFieldVector<T extends FieldElement<T>> implements FieldVector<T>, Serializable {
+
+ /**
+ * Serial version id
+ */
+ 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.
+ * <p>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</code> method ({@link #append(FieldElement)},
+ * {@link #append(FieldElement[])}, {@link #append(FieldVector)},
+ * {@link #append(SparseFieldVector)}) to gather data into this vector.</p>
+ * @param field field to which the elements belong
+ */
+ public SparseFieldVector(Field<T> field) {
+ this(field, 0);
+ }
+
+
+ /**
+ * Construct a (dimension)-length vector of zeros.
+ * @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 The original vector
+ * @param resize The amount to resize it
+ */
+ 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 The size of the vector
+ * @param expectedSize The 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 The set of values to create from
+ */
+ public SparseFieldVector(Field<T> field, T[] 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 The instance to copy from
+ */
+ public SparseFieldVector(SparseFieldVector<T> v) {
+ field = v.field;
+ virtualSize = v.getDimension();
+ entries = new OpenIntToFieldHashMap<T>(v.getEntries());
+ }
+
+ /**
+ * Get the entries of this instance.
+ * @return entries of this instance
+ */
+ private OpenIntToFieldHashMap<T> getEntries() {
+ return entries;
+ }
+
+ /**
+ * Optimized method to add sparse vectors.
+ * @param v vector to add
+ * @return The sum of <code>this</code> and <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public FieldVector<T> add(SparseFieldVector<T> v) throws IllegalArgumentException {
+ 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;
+
+ }
+
+
+ /** {@inheritDoc} */
+ public FieldVector<T> add(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ SparseFieldVector<T> res = new SparseFieldVector<T>(field,getDimension());
+ for (int i = 0; i < v.length; i++) {
+ res.setEntry(i, v[i].add(getEntry(i)));
+ }
+ 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 {
+ return append(v.toArray());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(T d) {
+ FieldVector<T> res = new SparseFieldVector<T>(this, 1);
+ res.setEntry(virtualSize, d);
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(T[] a) {
+ FieldVector<T> res = new SparseFieldVector<T>(this, a.length);
+ for (int i = 0; i < a.length; i++) {
+ res.setEntry(i + virtualSize, a[i]);
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> copy() {
+ return new SparseFieldVector<T>(this);
+ }
+
+ /** {@inheritDoc} */
+ public T dotProduct(FieldVector<T> v) throws IllegalArgumentException {
+ 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 T dotProduct(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ T res = field.getZero();
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ int idx = iter.key();
+ T value = field.getZero();
+ if (idx < v.length) {
+ value = v[idx];
+ }
+ res = res.add(value.multiply(iter.value()));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeDivide(FieldVector<T> v)
+ throws IllegalArgumentException {
+ 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> ebeDivide(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ 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[iter.key()]));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeMultiply(FieldVector<T> v)throws IllegalArgumentException {
+ 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} */
+ public FieldVector<T> ebeMultiply(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ 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[iter.key()]));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getData() {
+ T[] res = buildArray(virtualSize);
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res[iter.key()] = iter.value();
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return virtualSize;
+ }
+
+ /** {@inheritDoc} */
+ public T getEntry(int index) throws MatrixIndexException {
+ checkIndex(index);
+ return entries.get(index);
+ }
+
+ /** {@inheritDoc} */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getSubVector(int index, int n)
+ throws MatrixIndexException {
+ 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) {
+ return copy().mapAddToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAddToSelf(T d) {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, getEntry(i).add(d));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivide(T d) {
+ return copy().mapDivideToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivideToSelf(T d) {
+ 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() {
+ return copy().mapInvToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInvToSelf() {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, field.getOne().divide(getEntry(i)));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiply(T d) {
+ return copy().mapMultiplyToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiplyToSelf(T d) {
+ 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) {
+ return copy().mapSubtractToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtractToSelf(T d) {
+ 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 square matrix outer product between instance and v
+ * @throws IllegalArgumentException if v is not the same size as {@code this}
+ */
+ public FieldMatrix<T> outerProduct(SparseFieldVector<T> v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ SparseFieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, virtualSize);
+ 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(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ FieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, virtualSize);
+ 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 < virtualSize; col++) {
+ res.setEntry(row, col, value.multiply(v[col]));
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> outerProduct(FieldVector<T> v)
+ throws IllegalArgumentException {
+ if(v instanceof SparseFieldVector<?>)
+ return outerProduct((SparseFieldVector<T>)v);
+ else
+ return outerProduct(v.toArray());
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(FieldVector<T> v)
+ throws IllegalArgumentException {
+ checkVectorDimensions(v.getDimension());
+ return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ return projection(new SparseFieldVector<T>(field,v));
+ }
+
+ /** {@inheritDoc} */
+ public void set(T value) {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setEntry(int index, T value) throws MatrixIndexException {
+ checkIndex(index);
+ entries.put(index, value);
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, FieldVector<T> v)
+ throws MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ setSubVector(index, v.getData());
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, T[] v) throws MatrixIndexException {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ for (int i = 0; i < v.length; i++) {
+ setEntry(i + index, v[i]);
+ }
+
+ }
+
+ /**
+ * Optimized method to subtract SparseRealVectors.
+ * @param v The vector to subtract from <code>this</code>
+ * @return The difference of <code>this</code> and <code>v</code>
+ * @throws IllegalArgumentException If the dimensions don't match
+ */
+ public SparseFieldVector<T> subtract(SparseFieldVector<T> v) throws IllegalArgumentException{
+ 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 IllegalArgumentException {
+ if(v instanceof SparseFieldVector<?>)
+ return subtract((SparseFieldVector<T>)v);
+ else
+ return subtract(v.toArray());
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> subtract(T[] v) throws IllegalArgumentException {
+ checkVectorDimensions(v.length);
+ SparseFieldVector<T> res = new SparseFieldVector<T>(this);
+ for (int i = 0; i < v.length; i++) {
+ if (entries.containsKey(i)) {
+ res.setEntry(i, entries.get(i).subtract(v[i]));
+ } else {
+ res.setEntry(i, field.getZero().subtract(v[i]));
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public T[] toArray() {
+ return getData();
+ }
+
+ /**
+ * Check if an index is valid.
+ *
+ * @param index
+ * index to check
+ * @exception MatrixIndexException
+ * if index is not valid
+ */
+ private void checkIndex(final int index) throws MatrixIndexException {
+ if (index < 0 || index >= getDimension()) {
+ throw new MatrixIndexException(LocalizedFormats.INDEX_OUT_OF_RANGE,
+ index, 0, getDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n
+ * expected dimension.
+ * @exception IllegalArgumentException
+ * if the dimension is inconsistent with vector size
+ */
+ protected void checkVectorDimensions(int n) throws IllegalArgumentException {
+ if (getDimension() != n) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.VECTOR_LENGTH_MISMATCH,
+ getDimension(), n);
+ }
+ }
+
+
+ /** {@inheritDoc} */
+ public FieldVector<T> add(FieldVector<T> v) throws IllegalArgumentException {
+ if (v instanceof SparseFieldVector<?>) {
+ return add((SparseFieldVector<T>)v);
+ } else {
+ return add(v.toArray());
+ }
+ }
+
+ /** Build an array of elements.
+ * @param length size of the array to build
+ * @return a new array
+ */
+ @SuppressWarnings("unchecked") // field is type T
+ private T[] buildArray(final int length) {
+ return (T[]) Array.newInstance(field.getZero().getClass(), length);
+ }
+
+
+ /** {@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/math/linear/SparseRealMatrix.java b/src/main/java/org/apache/commons/math/linear/SparseRealMatrix.java
new file mode 100644
index 0000000..ad1fa19
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SparseRealMatrix.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.math.linear;
+
+/**
+ * Marker interface for {@link RealMatrix} implementations that require sparse backing storage
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ *
+ */
+public interface SparseRealMatrix extends RealMatrix {
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/SparseRealVector.java b/src/main/java/org/apache/commons/math/linear/SparseRealVector.java
new file mode 100644
index 0000000..adba604
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/SparseRealVector.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.
+ */
+package org.apache.commons.math.linear;
+
+/**
+ * Marker interface for RealVectors that require sparse backing storage
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ *
+ */
+public interface SparseRealVector extends RealVector {
+
+}
diff --git a/src/main/java/org/apache/commons/math/linear/TriDiagonalTransformer.java b/src/main/java/org/apache/commons/math/linear/TriDiagonalTransformer.java
new file mode 100644
index 0000000..9260aa0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/TriDiagonalTransformer.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.math.linear;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * 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>
+ * <p>This implementation only uses the upper part of the matrix, the part below the
+ * diagonal is not accessed at all.</p>
+ * <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.</p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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.</p>
+ * @param matrix the symmetrical matrix to transform.
+ * @exception InvalidMatrixException if matrix is not square
+ */
+ public TriDiagonalTransformer(RealMatrix matrix)
+ throws InvalidMatrixException {
+ 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.</p>
+ * @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.</p>
+ * @return the Q matrix
+ */
+ public RealMatrix getQT() {
+
+ if (cachedQt == null) {
+
+ final int m = householderVectors.length;
+ cachedQt = MatrixUtils.createRealMatrix(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];
+ final double inv = 1.0 / (secondary[k - 1] * hK[k]);
+ cachedQt.setEntry(k, k, 1);
+ if (hK[k] != 0.0) {
+ double beta = 1.0 / secondary[k - 1];
+ cachedQt.setEntry(k, k, 1 + beta * hK[k]);
+ for (int i = k + 1; i < m; ++i) {
+ cachedQt.setEntry(k, i, beta * hK[i]);
+ }
+ for (int j = k + 1; j < m; ++j) {
+ beta = 0;
+ for (int i = k + 1; i < m; ++i) {
+ beta += cachedQt.getEntry(j, i) * hK[i];
+ }
+ beta *= inv;
+ cachedQt.setEntry(j, k, beta * hK[k]);
+ for (int i = k + 1; i < m; ++i) {
+ cachedQt.addToEntry(j, i, beta * hK[i]);
+ }
+ }
+ }
+ }
+ cachedQt.setEntry(0, 0, 1);
+
+ }
+
+ // 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;
+ cachedT = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < m; ++i) {
+ cachedT.setEntry(i, i, main[i]);
+ if (i > 0) {
+ cachedT.setEntry(i, i - 1, secondary[i - 1]);
+ }
+ if (i < main.length - 1) {
+ cachedT.setEntry(i, i + 1, secondary[i]);
+ }
+ }
+
+ }
+
+ // 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.</p>
+ * @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.</p>
+ * @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.</p>
+ * @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.</p>
+ */
+ 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/math/linear/package.html b/src/main/java/org/apache/commons/math/linear/package.html
new file mode 100644
index 0000000..fc1034e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/linear/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Linear algebra support.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java b/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java
new file mode 100644
index 0000000..c17f436
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.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.math.ode;
+
+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;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.events.CombinedEventsManager;
+import org.apache.commons.math.ode.events.EventException;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.events.EventState;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Base class managing common boilerplate for all integrators.
+ * @version $Revision: 1073267 $ $Date: 2011-02-22 10:06:20 +0100 (mar. 22 févr. 2011) $
+ * @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;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int evaluations;
+
+ /** Differential equations to integrate. */
+ private transient FirstOrderDifferentialEquations equations;
+
+ /** 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;
+ setMaxEvaluations(-1);
+ resetEvaluations();
+ }
+
+ /** 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) {
+ eventsStates.add(new EventState(handler, maxCheckInterval, convergence, maxIterationCount));
+ }
+
+ /** {@inheritDoc} */
+ public Collection<EventHandler> getEventHandlers() {
+ final List<EventHandler> list = new ArrayList<EventHandler>();
+ for (EventState state : eventsStates) {
+ list.add(state.getEventHandler());
+ }
+ return Collections.unmodifiableCollection(list);
+ }
+
+ /** {@inheritDoc} */
+ public void clearEventHandlers() {
+ eventsStates.clear();
+ }
+
+ /** Check if dense output is needed.
+ * @return true if there is at least one event handler or if
+ * one of the step handlers requires dense output
+ */
+ protected boolean requiresDenseOutput() {
+ if (!eventsStates.isEmpty()) {
+ return true;
+ }
+ for (StepHandler handler : stepHandlers) {
+ if (handler.requiresDenseOutput()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentStepStart() {
+ return stepStart;
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentSignedStepsize() {
+ return stepSize;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** Reset the number of evaluations to zero.
+ */
+ protected void resetEvaluations() {
+ evaluations = 0;
+ }
+
+ /** Set the differential equations.
+ * @param equations differential equations to integrate
+ * @see #computeDerivatives(double, double[], double[])
+ */
+ protected void setEquations(final FirstOrderDifferentialEquations equations) {
+ this.equations = equations;
+ }
+
+ /** 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
+ * @throws DerivativeException this user-defined exception should be used if an error is
+ * is triggered by user code
+ */
+ public void computeDerivatives(final double t, final double[] y, final double[] yDot)
+ throws DerivativeException {
+ if (++evaluations > maxEvaluations) {
+ throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
+ }
+ equations.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.</p>
+ * @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
+ * @throws DerivativeException this exception is propagated to the caller if
+ * the underlying user function triggers one
+ * @exception IntegratorException if the value of one event state cannot be evaluated
+ * @since 2.2
+ */
+ protected double acceptStep(final AbstractStepInterpolator interpolator,
+ final double[] y, final double[] yDot, final double tEnd)
+ throws DerivativeException, IntegratorException {
+
+ try {
+ double previousT = interpolator.getGlobalPreviousTime();
+ final double currentT = interpolator.getGlobalCurrentTime();
+ resetOccurred = false;
+
+ // 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> occuringEvents = 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
+ occuringEvents.add(state);
+ }
+ }
+
+ while (!occuringEvents.isEmpty()) {
+
+ // handle the chronologically first event
+ final Iterator<EventState> iterator = occuringEvents.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);
+
+ // trigger the event
+ interpolator.setInterpolatedTime(eventT);
+ final double[] eventY = interpolator.getInterpolatedState();
+ currentEvent.stepAccepted(eventT, eventY);
+ isLastStep = currentEvent.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(eventY, 0, y, 0, y.length);
+ return eventT;
+ }
+
+ if (currentEvent.reset(eventT, eventY)) {
+ // some event handler has triggered changes that
+ // invalidate the derivatives, we need to recompute them
+ System.arraycopy(eventY, 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
+ occuringEvents.add(currentEvent);
+ }
+
+ }
+
+ interpolator.setInterpolatedTime(currentT);
+ final double[] currentY = interpolator.getInterpolatedState();
+ for (final EventState state : eventsStates) {
+ state.stepAccepted(currentT, currentY);
+ isLastStep = isLastStep || state.stop();
+ }
+ isLastStep = isLastStep || MathUtils.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;
+ } catch (EventException se) {
+ final Throwable cause = se.getCause();
+ if ((cause != null) && (cause instanceof DerivativeException)) {
+ throw (DerivativeException) cause;
+ }
+ throw new IntegratorException(se);
+ } catch (ConvergenceException ce) {
+ throw new IntegratorException(ce);
+ }
+
+ }
+
+ /** Perform some sanity checks on the integration parameters.
+ * @param ode differential equations set
+ * @param t0 start time
+ * @param y0 state vector at t0
+ * @param t target time for the integration
+ * @param y placeholder where to put the state vector
+ * @exception IntegratorException if some inconsistency is detected
+ */
+ protected void sanityChecks(final FirstOrderDifferentialEquations ode,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws IntegratorException {
+
+ if (ode.getDimension() != y0.length) {
+ throw new IntegratorException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y0.length);
+ }
+
+ if (ode.getDimension() != y.length) {
+ throw new IntegratorException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y.length);
+ }
+
+ if (FastMath.abs(t - t0) <= 1.0e-12 * FastMath.max(FastMath.abs(t0), FastMath.abs(t))) {
+ throw new IntegratorException(
+ LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL,
+ FastMath.abs(t - t0));
+ }
+
+ }
+
+ /** Add an event handler for end time checking.
+ * <p>This method can be used to simplify handling of integration end time.
+ * It leverages the nominal stop condition with the exceptional stop
+ * conditions.</p>
+ * @param startTime integration start time
+ * @param endTime desired end time
+ * @param manager manager containing the user-defined handlers
+ * @return a new manager containing all the user-defined handlers plus a
+ * dedicated manager triggering a stop event at entTime
+ * @deprecated as of 2.2, this method is not used any more
+ */
+ @Deprecated
+ protected CombinedEventsManager addEndTimeChecker(final double startTime,
+ final double endTime,
+ final CombinedEventsManager manager) {
+ CombinedEventsManager newManager = new CombinedEventsManager();
+ for (final EventState state : manager.getEventsStates()) {
+ newManager.addEventHandler(state.getEventHandler(),
+ state.getMaxCheckInterval(),
+ state.getConvergence(),
+ state.getMaxIterationCount());
+ }
+ newManager.addEventHandler(new EndTimeChecker(endTime),
+ Double.POSITIVE_INFINITY,
+ FastMath.ulp(FastMath.max(FastMath.abs(startTime), FastMath.abs(endTime))),
+ 100);
+ return newManager;
+ }
+
+ /** Specialized event handler to stop integration.
+ * @deprecated as of 2.2, this class is not used anymore
+ */
+ @Deprecated
+ private static class EndTimeChecker implements EventHandler {
+
+ /** Desired end time. */
+ private final double endTime;
+
+ /** Build an instance.
+ * @param endTime desired time
+ */
+ public EndTimeChecker(final double endTime) {
+ this.endTime = endTime;
+ }
+
+ /** {@inheritDoc} */
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ return STOP;
+ }
+
+ /** {@inheritDoc} */
+ public double g(double t, double[] y) {
+ return t - endTime;
+ }
+
+ /** {@inheritDoc} */
+ public void resetState(double t, double[] y) {
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/ContinuousOutputModel.java b/src/main/java/org/apache/commons/math/ode/ContinuousOutputModel.java
new file mode 100644
index 0000000..acafdcb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/ContinuousOutputModel.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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.math.ode.nonstiff.AdaptiveStepsizeIntegrator adaptive
+ * step size integrators}).</p>
+ *
+ * @see StepHandler
+ * @see StepInterpolator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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>();
+ reset();
+ }
+
+ /** Append another model at the end of the instance.
+ * @param model model to add at the end of the instance
+ * @exception DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ * @exception IllegalArgumentException if the model to append is not
+ * compatible with the instance (dimension of the state vector,
+ * propagation direction, hole between the dates)
+ */
+ public void append(final ContinuousOutputModel model)
+ throws DerivativeException {
+
+ if (model.steps.size() == 0) {
+ return;
+ }
+
+ if (steps.size() == 0) {
+ initialTime = model.initialTime;
+ forward = model.forward;
+ } else {
+
+ if (getInterpolatedState().length != model.getInterpolatedState().length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ getInterpolatedState().length, model.getInterpolatedState().length);
+ }
+
+ if (forward ^ model.forward) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 MathRuntimeException.createIllegalArgumentException(
+ 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();
+
+ }
+
+ /** Determines whether this handler needs dense output.
+ * <p>The essence of this class is to provide dense output over all
+ * steps, hence it requires the internal steps to provide themselves
+ * dense output. The method therefore returns always true.</p>
+ * @return always true
+ */
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+
+ /** Reset the step handler.
+ * Initialize the internal data as required before the first step is
+ * handled.
+ */
+ public void reset() {
+ 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 DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ */
+ public void handleStep(final StepInterpolator interpolator, final boolean isLast)
+ throws DerivativeException {
+
+ 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>
+ * <p>Setting the time outside of the integration interval is now
+ * allowed (it was not allowed up to version 5.9 of Mantissa), 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>
+ * @param time time of the interpolated point
+ */
+ 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.
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @exception DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ */
+ public double[] getInterpolatedState() throws DerivativeException {
+ return steps.get(index).getInterpolatedState();
+ }
+
+ /** 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/math/ode/DerivativeException.java b/src/main/java/org/apache/commons/math/ode/DerivativeException.java
new file mode 100644
index 0000000..28251ed
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/DerivativeException.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.math.ode;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This exception is made available to users to report
+ * the error conditions that are triggered while computing
+ * the differential equations.
+ * @version $Revision: 1072413 $ $Date: 2011-02-19 19:59:39 +0100 (sam. 19 févr. 2011) $
+ * @since 1.2
+ */
+public class DerivativeException extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5666710788967425123L;
+
+ /** 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)
+ */
+ public DerivativeException(final String specifier, final Object ... parts) {
+ this(new DummyLocalizable(specifier), parts);
+ }
+
+ /** 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 DerivativeException(final Localizable specifier, final Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /** Build an instance from an underlying cause.
+ * @param cause cause for the exception
+ */
+ public DerivativeException(final Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/ExtendedFirstOrderDifferentialEquations.java b/src/main/java/org/apache/commons/math/ode/ExtendedFirstOrderDifferentialEquations.java
new file mode 100644
index 0000000..8d0d634
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/ExtendedFirstOrderDifferentialEquations.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.math.ode;
+
+
+/** This interface represents a first order differential equations set
+ * with a main set of equations and an extension set.
+ *
+ * <p>
+ * This interface is a simple extension on the {@link
+ * FirstOrderDifferentialEquations} that allows to identify which part
+ * of a complete set of differential equations correspond to the main
+ * set and which part correspond to the extension set.
+ * </p>
+ * <p>
+ * One typical use case is the computation of Jacobians. The main
+ * set of equations correspond to the raw ode, and we add to this set
+ * another bunch of equations which represent the jacobians of the
+ * main set. In that case, we want the integrator to use <em>only</em>
+ * the main set to estimate the errors and hence the step sizes. It should
+ * <em>not</em> use the additional equations in this computation. If the
+ * complete ode implements this interface, the {@link FirstOrderIntegrator
+ * integrator} will be able to know where the main set ends and where the
+ * extended set begins.
+ * </p>
+ * <p>
+ * We consider that the main set always corresponds to the first equations
+ * and the extended set to the last equations.
+ * </p>
+ *
+ * @see FirstOrderDifferentialEquations
+ *
+ * @version $Revision: 980981 $ $Date: 2010-07-31 00:03:04 +0200 (sam. 31 juil. 2010) $
+ * @since 2.2
+ */
+
+public interface ExtendedFirstOrderDifferentialEquations extends FirstOrderDifferentialEquations {
+
+ /** Return the dimension of the main set of equations.
+ * <p>
+ * The main set of equations represent the first part of an ODE state.
+ * The error estimations and adaptive step size computation should be
+ * done on this first part only, not on the final part of the state
+ * which represent an extension set of equations which are considered
+ * secondary.
+ * </p>
+ * @return dimension of the main set of equations, must be lesser than or
+ * equal to the {@link #getDimension() total dimension}
+ */
+ int getMainSetDimension();
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/FirstOrderConverter.java b/src/main/java/org/apache/commons/math/ode/FirstOrderConverter.java
new file mode 100644
index 0000000..b4712e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/FirstOrderConverter.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.math.ode;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/** 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>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @see FirstOrderIntegrator
+ * @see FirstOrderDifferentialEquations
+ * @see SecondOrderDifferentialEquations
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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.</p>
+ * @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
+ * @throws DerivativeException this exception is propagated to the caller if the
+ * underlying user function triggers one
+ */
+ public void computeDerivatives(final double t, final double[] y, final double[] yDot)
+ throws DerivativeException {
+
+ // 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/math/ode/FirstOrderDifferentialEquations.java b/src/main/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.java
new file mode 100644
index 0000000..7c6aacc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/FirstOrderDifferentialEquations.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.math.ode;
+
+
+
+/** 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>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @see FirstOrderIntegrator
+ * @see FirstOrderConverter
+ * @see SecondOrderDifferentialEquations
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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
+ * @throws DerivativeException this user-defined exception should be used if an error is
+ * is triggered by user code
+ */
+ void computeDerivatives(double t, double[] y, double[] yDot)
+ throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/FirstOrderIntegrator.java b/src/main/java/org/apache/commons/math/ode/FirstOrderIntegrator.java
new file mode 100644
index 0000000..b833084
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/FirstOrderIntegrator.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.math.ode;
+
+
+/** 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.</p>
+ *
+ * @see FirstOrderDifferentialEquations
+ * @see org.apache.commons.math.ode.sampling.StepHandler
+ * @see org.apache.commons.math.ode.events.EventHandler
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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>
+ * <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.</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</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.math.ode.events.EventHandler} stops it at some point.
+ * @throws DerivativeException this exception is propagated to the caller if
+ * the underlying user function triggers one
+ * @throws IntegratorException if the integrator cannot perform integration
+ */
+ double integrate (FirstOrderDifferentialEquations equations,
+ double t0, double[] y0,
+ double t, double[] y) throws DerivativeException, IntegratorException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/IntegratorException.java b/src/main/java/org/apache/commons/math/ode/IntegratorException.java
new file mode 100644
index 0000000..b116225
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/IntegratorException.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.math.ode;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This exception is made available to users to report
+ * the error conditions that are triggered during integration
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 1.2
+ */
+public class IntegratorException
+ extends MathException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1607588949778036796L;
+
+ /** 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)
+ * @deprecated as of 2.2 replaced by {@link #IntegratorException(Localizable, Object...)}
+ */
+ @Deprecated
+ public IntegratorException(final String specifier, final Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /** 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 IntegratorException(final Localizable specifier, final Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /**
+ * Create an exception with a given root cause.
+ * @param cause the exception or error that caused this exception to be thrown
+ */
+ public IntegratorException(final Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/MultistepIntegrator.java b/src/main/java/org/apache/commons/math/ode/MultistepIntegrator.java
new file mode 100644
index 0000000..1794878
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/MultistepIntegrator.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.math.ode;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.ode.nonstiff.AdaptiveStepsizeIntegrator;
+import org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.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(k)<sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ * <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>
+ * <p>
+ * Multistep integrators with Nordsieck representation are highly sensitive to
+ * large step changes because when the step is multiplied by a 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>.
+ * </p>
+ *
+ * @see org.apache.commons.math.ode.nonstiff.AdamsBashforthIntegrator
+ * @see org.apache.commons.math.ode.nonstiff.AdamsMoultonIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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(k))</p>
+ */
+ 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>
+ * <p>
+ * The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ * </p>
+ * @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
+ */
+ protected MultistepIntegrator(final String name, final int nSteps,
+ final int order,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ if (nSteps <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INTEGRATION_METHOD_NEEDS_AT_LEAST_ONE_PREVIOUS_POINT,
+ name);
+ }
+
+ 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>
+ * <p>
+ * The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ * </p>
+ * @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.</p>
+ * @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.</p>
+ * @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)
+ * @throws IntegratorException if the integrator cannot perform integration
+ * @throws DerivativeException this exception is propagated to the caller if
+ * the underlying user function triggers one
+ */
+ protected void start(final double t0, final double[] y0, final double t)
+ throws DerivativeException, IntegratorException {
+
+ // 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(y0.length));
+
+ // start integration, expecting a InitializationCompletedMarkerException
+ try {
+ starter.integrate(new CountingDifferentialEquations(y0.length),
+ t0, y0, t, new double[y0.length]);
+ } catch (DerivativeException mue) {
+ if (!(mue instanceof InitializationCompletedMarkerException)) {
+ // this is not the expected nominal interruption of the start integrator
+ throw mue;
+ }
+ }
+
+ // remove the specific step handler
+ starter.clearStepHandlers();
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param first first scaled derivative at step start
+ * @param multistep scaled derivatives after step start (hy'1, ..., hy'k-1)
+ * will be modified
+ * @return high order scaled derivatives at step start
+ */
+ protected abstract Array2DRowRealMatrix initializeHighOrderDerivatives(final double[] first,
+ final double[][] multistep);
+
+ /** 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;
+ }
+
+ /** 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. */
+ public static interface NordsieckTransformer {
+ /** Initialize the high order scaled derivatives at step start.
+ * @param first first scaled derivative at step start
+ * @param multistep scaled derivatives after step start (hy'1, ..., hy'k-1)
+ * will be modified
+ * @return high order derivatives at step start
+ */
+ RealMatrix initializeHighOrderDerivatives(double[] first, double[][] multistep);
+ }
+
+ /** Specialized step handler storing the first step. */
+ private class NordsieckInitializer implements StepHandler {
+
+ /** Problem dimension. */
+ private final int n;
+
+ /** Simple constructor.
+ * @param n problem dimension
+ */
+ public NordsieckInitializer(final int n) {
+ this.n = n;
+ }
+
+ /** {@inheritDoc} */
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+
+ final double prev = interpolator.getPreviousTime();
+ final double curr = interpolator.getCurrentTime();
+ stepStart = prev;
+ stepSize = (curr - prev) / (nSteps + 1);
+
+ // compute the first scaled derivative
+ interpolator.setInterpolatedTime(prev);
+ scaled = interpolator.getInterpolatedDerivatives().clone();
+ for (int j = 0; j < n; ++j) {
+ scaled[j] *= stepSize;
+ }
+
+ // compute the high order scaled derivatives
+ final double[][] multistep = new double[nSteps][];
+ for (int i = 1; i <= nSteps; ++i) {
+ interpolator.setInterpolatedTime(prev + stepSize * i);
+ final double[] msI = interpolator.getInterpolatedDerivatives().clone();
+ for (int j = 0; j < n; ++j) {
+ msI[j] *= stepSize;
+ }
+ multistep[i - 1] = msI;
+ }
+ nordsieck = initializeHighOrderDerivatives(scaled, multistep);
+
+ // stop the integrator after the first step has been handled
+ throw new InitializationCompletedMarkerException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ // nothing to do
+ }
+
+ }
+
+ /** Marker exception used ONLY to stop the starter integrator after first step. */
+ private static class InitializationCompletedMarkerException
+ extends DerivativeException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4105805787353488365L;
+
+ /** Simple constructor. */
+ public InitializationCompletedMarkerException() {
+ super((Throwable) null);
+ }
+
+ }
+
+ /** Wrapper for differential equations, ensuring start evaluations are counted. */
+ private class CountingDifferentialEquations implements ExtendedFirstOrderDifferentialEquations {
+
+ /** Dimension of the problem. */
+ private final int dimension;
+
+ /** Simple constructor.
+ * @param dimension dimension of the problem
+ */
+ public CountingDifferentialEquations(final int dimension) {
+ this.dimension = dimension;
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(double t, double[] y, double[] dot)
+ throws DerivativeException {
+ MultistepIntegrator.this.computeDerivatives(t, y, dot);
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /** {@inheritDoc} */
+ public int getMainSetDimension() {
+ return mainSetDimension;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/ODEIntegrator.java b/src/main/java/org/apache/commons/math/ode/ODEIntegrator.java
new file mode 100644
index 0000000..6343478
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/ODEIntegrator.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.math.ode;
+
+import java.util.Collection;
+
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.sampling.StepHandler;
+
+/**
+ * This interface defines the common parts shared by integrators
+ * for first and second order differential equations.
+ * @see FirstOrderIntegrator
+ * @see SecondOrderIntegrator
+ * @version $Revision: 1061507 $ $Date: 2011-01-20 21:55:00 +0100 (jeu. 20 janv. 2011) $
+ * @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.</p>
+ * @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.
+ * @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);
+
+ /** 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>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @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>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @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.</p>
+ * @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.
+ * </p>
+ * @return number of evaluations of the differential equations function
+ */
+ int getEvaluations();
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/SecondOrderDifferentialEquations.java b/src/main/java/org/apache/commons/math/ode/SecondOrderDifferentialEquations.java
new file mode 100644
index 0000000..d5e7324
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/SecondOrderDifferentialEquations.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.math.ode;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/** 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>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @see SecondOrderIntegrator
+ * @see FirstOrderConverter
+ * @see FirstOrderDifferentialEquations
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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
+ * @throws DerivativeException this user-defined exception should be used if an error is
+ * is triggered by user code
+ */
+ void computeSecondDerivatives(double t, double[] y, double[] yDot, double[] yDDot)
+ throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/SecondOrderIntegrator.java b/src/main/java/org/apache/commons/math/ode/SecondOrderIntegrator.java
new file mode 100644
index 0000000..f744e85
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/SecondOrderIntegrator.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.math.ode;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+
+/** 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.</p>
+ *
+ * @see SecondOrderDifferentialEquations
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 IntegratorException if the integrator cannot perform integration
+ * @throws DerivativeException this exception is propagated to the caller if the
+ * underlying user function triggers one
+ */
+ void integrate(SecondOrderDifferentialEquations equations,
+ double t0, double[] y0, double[] yDot0,
+ double t, double[] y, double[] yDot)
+ throws DerivativeException, IntegratorException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/events/CombinedEventsManager.java b/src/main/java/org/apache/commons/math/ode/events/CombinedEventsManager.java
new file mode 100644
index 0000000..1886702
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/events/CombinedEventsManager.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.math.ode.events;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+
+/** This class manages several {@link EventHandler event handlers} during integration.
+ *
+ * @see EventHandler
+ * @see EventState
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ * @deprecated as of 2.2, this class is not used anymore
+ */
+@Deprecated
+public class CombinedEventsManager {
+
+ /** Events states. */
+ private final List<EventState> states;
+
+ /** First active event. */
+ private EventState first;
+
+ /** Initialization indicator. */
+ private boolean initialized;
+
+ /** Simple constructor.
+ * Create an empty manager
+ */
+ public CombinedEventsManager() {
+ states = new ArrayList<EventState>();
+ first = null;
+ initialized = false;
+ }
+
+ /** Add an events handler.
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between events
+ * 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 #getEventsHandlers()
+ * @see #clearEventsHandlers()
+ */
+ public void addEventHandler(final EventHandler handler, final double maxCheckInterval,
+ final double convergence, final int maxIterationCount) {
+ states.add(new EventState(handler, maxCheckInterval,
+ convergence, maxIterationCount));
+ }
+
+ /** Get all the events handlers that have been added to the manager.
+ * @return an unmodifiable collection of the added event handlers
+ * @see #addEventHandler(EventHandler, double, double, int)
+ * @see #clearEventsHandlers()
+ * @see #getEventsStates()
+ */
+ public Collection<EventHandler> getEventsHandlers() {
+ final List<EventHandler> list = new ArrayList<EventHandler>();
+ for (EventState state : states) {
+ list.add(state.getEventHandler());
+ }
+ return Collections.unmodifiableCollection(list);
+ }
+
+ /** Remove all the events handlers that have been added to the manager.
+ * @see #addEventHandler(EventHandler, double, double, int)
+ * @see #getEventsHandlers()
+ */
+ public void clearEventsHandlers() {
+ states.clear();
+ }
+
+ /** Get all the events state wrapping the handlers that have been added to the manager.
+ * @return a collection of the events states
+ * @see #getEventsHandlers()
+ */
+ public Collection<EventState> getEventsStates() {
+ return states;
+ }
+
+ /** Check if the manager does not manage any event handlers.
+ * @return true if manager is empty
+ */
+ public boolean isEmpty() {
+ return states.isEmpty();
+ }
+
+ /** Evaluate the impact of the proposed step on all managed
+ * event handlers.
+ * @param interpolator step interpolator for the proposed step
+ * @return true if at least one event handler triggers an event
+ * before the end of the proposed step (this implies the step should
+ * be rejected)
+ * @exception DerivativeException if the interpolator fails to
+ * compute the function somewhere within the step
+ * @exception IntegratorException if an event cannot be located
+ */
+ public boolean evaluateStep(final StepInterpolator interpolator)
+ throws DerivativeException, IntegratorException {
+
+ try {
+
+ first = null;
+ if (states.isEmpty()) {
+ // there is nothing to do, return now to avoid setting the
+ // interpolator time (and hence avoid unneeded calls to the
+ // user function due to interpolator finalization)
+ return false;
+ }
+
+ if (! initialized) {
+
+ // initialize the events states
+ for (EventState state : states) {
+ state.reinitializeBegin(interpolator);
+ }
+
+ initialized = true;
+
+ }
+
+ // check events occurrence
+ for (EventState state : states) {
+
+ if (state.evaluateStep(interpolator)) {
+ if (first == null) {
+ first = state;
+ } else {
+ if (interpolator.isForward()) {
+ if (state.getEventTime() < first.getEventTime()) {
+ first = state;
+ }
+ } else {
+ if (state.getEventTime() > first.getEventTime()) {
+ first = state;
+ }
+ }
+ }
+ }
+
+ }
+
+ return first != null;
+
+ } catch (EventException se) {
+ final Throwable cause = se.getCause();
+ if ((cause != null) && (cause instanceof DerivativeException)) {
+ throw (DerivativeException) cause;
+ }
+ throw new IntegratorException(se);
+ } catch (ConvergenceException ce) {
+ throw new IntegratorException(ce);
+ }
+
+ }
+
+ /** Get the occurrence time of the first event triggered in the
+ * last evaluated step.
+ * @return occurrence time of the first event triggered in the last
+ * evaluated step, or </code>Double.NaN</code> if no event is
+ * triggered
+ */
+ public double getEventTime() {
+ return (first == null) ? Double.NaN : first.getEventTime();
+ }
+
+ /** Inform the event handlers that 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
+ * @exception IntegratorException if the value of one of the
+ * events states cannot be evaluated
+ */
+ public void stepAccepted(final double t, final double[] y)
+ throws IntegratorException {
+ try {
+ for (EventState state : states) {
+ state.stepAccepted(t, y);
+ }
+ } catch (EventException se) {
+ throw new IntegratorException(se);
+ }
+ }
+
+ /** 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() {
+ for (EventState state : states) {
+ if (state.stop()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Let the event handlers reset the state if they want.
+ * @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
+ * @exception IntegratorException if one of the events states
+ * that should reset the state fails to do it
+ */
+ public boolean reset(final double t, final double[] y)
+ throws IntegratorException {
+ try {
+ boolean resetDerivatives = false;
+ for (EventState state : states) {
+ if (state.reset(t, y)) {
+ resetDerivatives = true;
+ }
+ }
+ return resetDerivatives;
+ } catch (EventException se) {
+ throw new IntegratorException(se);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/events/EventException.java b/src/main/java/org/apache/commons/math/ode/events/EventException.java
new file mode 100644
index 0000000..92e8e0c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/events/EventException.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.math.ode.events;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This exception is made available to users to report
+ * the error conditions that are triggered by {@link EventHandler}
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class EventException extends MathException {
+
+ /** Serialization UID. */
+ private static final long serialVersionUID = -898215297400035290L;
+
+ /** 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)
+ * @deprecated as of 2.2 replaced by {@link #EventException(Localizable, Object...)}
+ */
+ @Deprecated
+ public EventException(final String specifier, final Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /** 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 EventException(final Localizable specifier, final Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /**
+ * Create an exception with a given root cause.
+ * @param cause the exception or error that caused this exception to be thrown
+ */
+ public EventException(final Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/events/EventHandler.java b/src/main/java/org/apache/commons/math/ode/events/EventHandler.java
new file mode 100644
index 0000000..4e50db6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/events/EventHandler.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.math.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>
+ *
+ * @version $Revision: 1067500 $ $Date: 2011-02-05 21:11:30 +0100 (sam. 05 févr. 2011) $
+ * @since 1.2
+ */
+
+public interface EventHandler {
+
+ /** 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>
+ */
+ int STOP = 0;
+
+ /** 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>
+ */
+ int RESET_STATE = 1;
+
+ /** 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.math.ode.FirstOrderDifferentialEquations#computeDerivatives}
+ * method).</p>
+ */
+ int RESET_DERIVATIVES = 2;
+
+ /** 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>
+ */
+ int CONTINUE = 3;
+
+ /** 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>
+ *
+ * @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
+ * @exception EventException if the switching function cannot be evaluated
+ */
+ double g(double t, double[] y) throws EventException;
+
+ /** 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.math.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 #STOP} is returned, the step handler will be called
+ * with the <code>isLast</code> flag of the {@link
+ * org.apache.commons.math.ode.sampling.StepHandler#handleStep handleStep}
+ * method set to true and the integration will be stopped,</li>
+ * <li>if {@link #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 #RESET_DERIVATIVES} is returned, the integrator
+ * will recompute the derivatives,
+ * <li>if {@link #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.math.ode.sampling.StepHandler StepHandler} method {@link
+ * org.apache.commons.math.ode.sampling.StepHandler#handleStep(
+ * org.apache.commons.math.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 #STOP}. As the interpolator may be used to navigate back
+ * throughout the last step (as {@link
+ * org.apache.commons.math.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.math.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.math.ode.sampling.StepHandler variable step handlers} and
+ * to the size of the fixed step for {@link
+ * org.apache.commons.math.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 #STOP}, {@link #RESET_STATE},
+ * {@link #RESET_DERIVATIVES} or {@link #CONTINUE}
+ * @exception EventException if the event occurrence triggers an error
+ */
+ int eventOccurred(double t, double[] y, boolean increasing) throws EventException;
+
+ /** 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 #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
+ * #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
+ * @exception EventException if the state cannot be reseted
+ */
+ void resetState(double t, double[] y) throws EventException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/events/EventState.java b/src/main/java/org/apache/commons/math/ode/events/EventState.java
new file mode 100644
index 0000000..30cb47e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.ode.events;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.solvers.BrentSolver;
+import org.apache.commons.math.exception.MathInternalError;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.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 (and hence the step should be reduced to ensure the
+ * event occurs at a bound rather than inside the step).</p>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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;
+
+ /** 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 int nextAction;
+
+ /** 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
+ */
+ public EventState(final EventHandler handler, final double maxCheckInterval,
+ final double convergence, final int maxIterationCount) {
+ this.handler = handler;
+ this.maxCheckInterval = maxCheckInterval;
+ this.convergence = FastMath.abs(convergence);
+ this.maxIterationCount = maxIterationCount;
+
+ // some dummy values ...
+ t0 = Double.NaN;
+ g0 = Double.NaN;
+ g0Positive = true;
+ pendingEvent = false;
+ pendingEventTime = Double.NaN;
+ previousEventTime = Double.NaN;
+ increasing = true;
+ nextAction = EventHandler.CONTINUE;
+
+ }
+
+ /** Get the underlying event handler.
+ * @return underlying event handler
+ */
+ public EventHandler 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 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 EventException if the event handler
+ * value cannot be evaluated at the beginning of the step
+ */
+ public void reinitializeBegin(final StepInterpolator interpolator)
+ throws EventException {
+ try {
+ // 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 want 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
+ // less than epsilon after the solver start in the first step should be ignored,
+ // where epsilon is the convergence threshold of the event. The sign of the g
+ // function should be evaluated after this initial ignore zone, not exactly at
+ // beginning (if there are no event at the very beginning g(t0) and g(t0+epsilon)
+ // have the same sign, so this does not hurt ; if there is an event at the very
+ // beginning, g(t0) and g(t0+epsilon) have opposite signs and we want to start
+ // with the second one. Of course, the sign of epsilon depend on the integration
+ // direction (forward or backward). This explains what is done below.
+
+ final double ignoreZone = interpolator.isForward() ? getConvergence() : -getConvergence();
+ t0 = interpolator.getPreviousTime() + ignoreZone;
+ interpolator.setInterpolatedTime(t0);
+ g0 = handler.g(t0, interpolator.getInterpolatedState());
+ if (g0 == 0) {
+ // extremely rare case: there is a zero EXACTLY at end of ignore zone
+ // we will use the opposite of sign at step beginning to force ignoring this zero
+ final double tStart = interpolator.getPreviousTime();
+ interpolator.setInterpolatedTime(tStart);
+ g0Positive = handler.g(tStart, interpolator.getInterpolatedState()) <= 0;
+ } else {
+ g0Positive = g0 >= 0;
+ }
+
+ } catch (DerivativeException mue) {
+ throw new EventException(mue);
+ }
+ }
+
+ /** 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 DerivativeException if the interpolator fails to
+ * compute the switching function somewhere within the step
+ * @exception EventException if the switching function
+ * cannot be evaluated
+ * @exception ConvergenceException if an event cannot be located
+ */
+ public boolean evaluateStep(final StepInterpolator interpolator)
+ throws DerivativeException, EventException, ConvergenceException {
+
+ try {
+
+ forward = interpolator.isForward();
+ final double t1 = interpolator.getCurrentTime();
+ if (FastMath.abs(t1 - t0) < convergence) {
+ // we cannot do anything on such a small step, don't trigger any events
+ return false;
+ }
+ final double start = forward ? (t0 + convergence) : t0 - convergence;
+ final double dt = t1 - start;
+ final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt) / maxCheckInterval));
+ final double h = dt / n;
+
+ 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 = start + (i + 1) * h;
+ interpolator.setInterpolatedTime(tb);
+ final double gb = handler.g(tb, interpolator.getInterpolatedState());
+
+ // 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;
+
+ final UnivariateRealFunction f = new UnivariateRealFunction() {
+ public double value(final double t) {
+ try {
+ interpolator.setInterpolatedTime(t);
+ return handler.g(t, interpolator.getInterpolatedState());
+ } catch (DerivativeException e) {
+ throw new EmbeddedDerivativeException(e);
+ } catch (EventException e) {
+ throw new EmbeddedEventException(e);
+ }
+ }
+ };
+ final BrentSolver solver = new BrentSolver(convergence);
+
+ if (ga * gb >= 0) {
+ // this is a corner case:
+ // - there was an event near ta,
+ // - there is another event between ta and tb
+ // - when ta was computed, convergence was reached on the "wrong side" of the interval
+ // this implies that the real sign of ga is the same as gb, so we need to slightly
+ // shift ta to make sure ga and gb get opposite signs and the solver won't complain
+ // about bracketing
+ final double epsilon = (forward ? 0.25 : -0.25) * convergence;
+ for (int k = 0; (k < 4) && (ga * gb > 0); ++k) {
+ ta += epsilon;
+ try {
+ ga = f.value(ta);
+ } catch (FunctionEvaluationException ex) {
+ throw new DerivativeException(ex);
+ }
+ }
+ if (ga * gb > 0) {
+ // this should never happen
+ throw new MathInternalError();
+ }
+ }
+
+ final double root;
+ try {
+ root = (ta <= tb) ?
+ solver.solve(maxIterationCount, f, ta, tb) :
+ solver.solve(maxIterationCount, f, tb, ta);
+ } catch (FunctionEvaluationException ex) {
+ throw new DerivativeException(ex);
+ }
+
+ 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, we simply ignore it
+ ta = tb;
+ ga = gb;
+ } 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 (EmbeddedDerivativeException ede) {
+ throw ede.getDerivativeException();
+ } catch (EmbeddedEventException eee) {
+ throw eee.getEventException();
+ }
+
+ }
+
+ /** Get the occurrence time of the event triggered in the current step.
+ * @return occurrence time of the event triggered in the current
+ * step or positive infinity if no events are triggered
+ */
+ public double getEventTime() {
+ return pendingEvent ? pendingEventTime : Double.POSITIVE_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
+ * @exception EventException if the value of the event
+ * handler cannot be evaluated
+ */
+ public void stepAccepted(final double t, final double[] y)
+ throws EventException {
+
+ 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.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.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
+ * @exception EventException if the state cannot be reseted by the event
+ * handler
+ */
+ public boolean reset(final double t, final double[] y)
+ throws EventException {
+
+ if (!(pendingEvent && (FastMath.abs(pendingEventTime - t) <= convergence))) {
+ return false;
+ }
+
+ if (nextAction == EventHandler.RESET_STATE) {
+ handler.resetState(t, y);
+ }
+ pendingEvent = false;
+ pendingEventTime = Double.NaN;
+
+ return (nextAction == EventHandler.RESET_STATE) ||
+ (nextAction == EventHandler.RESET_DERIVATIVES);
+
+ }
+
+ /** Local exception for embedding DerivativeException. */
+ private static class EmbeddedDerivativeException extends RuntimeException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 3574188382434584610L;
+
+ /** Embedded exception. */
+ private final DerivativeException derivativeException;
+
+ /** Simple constructor.
+ * @param derivativeException embedded exception
+ */
+ public EmbeddedDerivativeException(final DerivativeException derivativeException) {
+ this.derivativeException = derivativeException;
+ }
+
+ /** Get the embedded exception.
+ * @return embedded exception
+ */
+ public DerivativeException getDerivativeException() {
+ return derivativeException;
+ }
+
+ }
+
+ /** Local exception for embedding EventException. */
+ private static class EmbeddedEventException extends RuntimeException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = -1337749250090455474L;
+
+ /** Embedded exception. */
+ private final EventException eventException;
+
+ /** Simple constructor.
+ * @param eventException embedded exception
+ */
+ public EmbeddedEventException(final EventException eventException) {
+ this.eventException = eventException;
+ }
+
+ /** Get the embedded exception.
+ * @return embedded exception
+ */
+ public EventException getEventException() {
+ return eventException;
+ }
+
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/ode/events/package.html b/src/main/java/org/apache/commons/math/ode/events/package.html
new file mode 100644
index 0000000..68dcd97
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/events/package.html
@@ -0,0 +1,96 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 613620 $ -->
+<body>
+<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.math.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) * (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>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/EventHandlerWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/EventHandlerWithJacobians.java
new file mode 100644
index 0000000..80d6c6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/EventHandlerWithJacobians.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.math.ode.jacobians;
+
+import org.apache.commons.math.ode.events.EventException;
+
+/** 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>
+ *
+ * <p>Note that is is possible to register a {@link
+ * org.apache.commons.math.ode.events.EventHandler classical event handler}
+ * in the low level integrator used to build a {@link FirstOrderIntegratorWithJacobians}
+ * rather than implementing this class. The event handlers registered at low level
+ * will see the big compound state whether the event handlers defined by this interface
+ * see the original state, and its jacobians in separate arrays.</p>
+ *
+ * <p>The compound state is guaranteed to contain the original state in the first
+ * elements, followed by the jacobian with respect to initial state (in row order),
+ * followed by the jacobian with respect to parameters (in row order). If for example
+ * the original state dimension is 6 and there are 3 parameters, the compound state will
+ * be a 60 elements array. The first 6 elements will be the original state, the next 36
+ * elements will be the jacobian with respect to initial state, and the remaining 18 elements
+ * will be the jacobian with respect to parameters.</p>
+ *
+ * <p>Dealing with low level event handlers is cumbersome if one really needs the jacobians
+ * in these methods, but it also prevents many data being copied back and forth between
+ * state and jacobians on one side and compound state on the other side. So for performance
+ * reasons, it is recommended to use this interface <em>only</em> if jacobians are really
+ * needed and to use lower level handlers if only state is needed.</p>
+ *
+ * @version $Revision: 1037341 $ $Date: 2010-11-20 22:58:35 +0100 (sam. 20 nov. 2010) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public interface EventHandlerWithJacobians {
+
+ /** 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>
+ */
+ int STOP = 0;
+
+ /** 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>
+ */
+ int RESET_STATE = 1;
+
+ /** 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.math.ode.FirstOrderDifferentialEquations#computeDerivatives}
+ * method).</p>
+ */
+ int RESET_DERIVATIVES = 2;
+
+ /** 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>
+ */
+ int CONTINUE = 3;
+
+ /** 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>
+
+ * @param t current value of the independent <i>time</i> variable
+ * @param y array containing the current value of the state vector
+ * @param dydy0 array containing the current value of the jacobian of
+ * the state vector with respect to initial state
+ * @param dydp array containing the current value of the jacobian of
+ * the state vector with respect to parameters
+ * @return value of the g switching function
+ * @exception EventException if the switching function cannot be evaluated
+ */
+ double g(double t, double[] y, double[][] dydy0, double[][] dydp)
+ throws EventException;
+
+ /** 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.math.ode.jacobians.ODEWithJacobians
+ * 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 #STOP} is returned, the step handler will be called
+ * with the <code>isLast</code> flag of the {@link
+ * org.apache.commons.math.ode.jacobians.StepHandlerWithJacobians#handleStep(
+ * StepInterpolatorWithJacobians, boolean) handleStep} method set to true and
+ * the integration will be stopped,</li>
+ * <li>if {@link #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 #RESET_DERIVATIVES} is returned, the integrator
+ * will recompute the derivatives,
+ * <li>if {@link #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.math.ode.jacobians.StepHandlerWithJacobians
+ * StepHandlerWithJacobians} method {@link
+ * org.apache.commons.math.ode.jacobians.StepHandlerWithJacobians#handleStep(
+ * StepInterpolatorWithJacobians, 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 #STOP}. As the
+ * interpolator may be used to navigate back throughout the last step (as {@link
+ * org.apache.commons.math.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 EventHandlerWithJacobians EventHandler} interface and the
+ * {@link org.apache.commons.math.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.math.ode.sampling.StepHandler variable step handlers} and
+ * to the size of the fixed step for {@link
+ * org.apache.commons.math.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 dydy0 array containing the current value of the jacobian of
+ * the state vector with respect to initial state
+ * @param dydp array containing the current value of the jacobian of
+ * the state vector with respect to parameters
+ * @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 #STOP}, {@link #RESET_STATE},
+ * {@link #RESET_DERIVATIVES} or {@link #CONTINUE}
+ * @exception EventException if the event occurrence triggers an error
+ */
+ int eventOccurred(double t, double[] y, double[][] dydy0, double[][] dydp,
+ boolean increasing) throws EventException;
+
+ /** 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 #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
+ * #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
+ * @param dydy0 array containing the current value of the jacobian of
+ * the state vector with respect to initial state, the new jacobian
+ * should be put in the same array
+ * @param dydp array containing the current value of the jacobian of
+ * the state vector with respect to parameters, the new jacobian
+ * should be put in the same array
+ * @exception EventException if the state cannot be reseted
+ */
+ void resetState(double t, double[] y, double[][] dydy0, double[][] dydp)
+ throws EventException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java
new file mode 100644
index 0000000..dc5ca18
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobians.java
@@ -0,0 +1,899 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.jacobians;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.ExtendedFirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.events.EventException;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+
+/** This class enhances a first order integrator for differential equations to
+ * compute also partial derivatives of the solution with respect to initial state
+ * and parameters.
+ * <p>In order to compute both the state and its derivatives, the ODE problem
+ * is extended with jacobians of the raw ODE and the variational equations are
+ * added to form a new compound problem of higher dimension. If the original ODE
+ * problem has dimension n and there are p parameters, the compound problem will
+ * have dimension n &times; (1 + n + p).</p>
+ * @see ParameterizedODE
+ * @see ODEWithJacobians
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public class FirstOrderIntegratorWithJacobians {
+
+ /** Underlying integrator for compound problem. */
+ private final FirstOrderIntegrator integrator;
+
+ /** Raw equations to integrate. */
+ private final ODEWithJacobians ode;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int evaluations;
+
+ /** Build an enhanced integrator using internal differentiation to compute jacobians.
+ * @param integrator underlying integrator to solve the compound problem
+ * @param ode original problem (f in the equation y' = f(t, y))
+ * @param p parameters array (may be null if {@link
+ * ParameterizedODE#getParametersDimension()
+ * getParametersDimension()} from original problem is zero)
+ * @param hY step sizes to use for computing the jacobian df/dy, must have the
+ * same dimension as the original problem
+ * @param hP step sizes to use for computing the jacobian df/dp, must have the
+ * same dimension as the original problem parameters dimension
+ * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
+ * ODEWithJacobians)
+ */
+ public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
+ final ParameterizedODE ode,
+ final double[] p, final double[] hY, final double[] hP) {
+ checkDimension(ode.getDimension(), hY);
+ checkDimension(ode.getParametersDimension(), p);
+ checkDimension(ode.getParametersDimension(), hP);
+ this.integrator = integrator;
+ this.ode = new FiniteDifferencesWrapper(ode, p, hY, hP);
+ setMaxEvaluations(-1);
+ }
+
+ /** Build an enhanced integrator using ODE builtin jacobian computation features.
+ * @param integrator underlying integrator to solve the compound problem
+ * @param ode original problem, which can compute the jacobians by itself
+ * @see #FirstOrderIntegratorWithJacobians(FirstOrderIntegrator,
+ * ParameterizedODE, double[], double[], double[])
+ */
+ public FirstOrderIntegratorWithJacobians(final FirstOrderIntegrator integrator,
+ final ODEWithJacobians ode) {
+ this.integrator = integrator;
+ this.ode = ode;
+ setMaxEvaluations(-1);
+ }
+
+ /** Add a step handler to this integrator.
+ * <p>The handler will be called by the integrator for each accepted
+ * step.</p>
+ * @param handler handler for the accepted steps
+ * @see #getStepHandlers()
+ * @see #clearStepHandlers()
+ */
+ public void addStepHandler(StepHandlerWithJacobians handler) {
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ integrator.addStepHandler(new StepHandlerWrapper(handler, n, k));
+ }
+
+ /** Get all the step handlers that have been added to the integrator.
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addStepHandler(StepHandlerWithJacobians)
+ * @see #clearStepHandlers()
+ */
+ public Collection<StepHandlerWithJacobians> getStepHandlers() {
+ final Collection<StepHandlerWithJacobians> handlers =
+ new ArrayList<StepHandlerWithJacobians>();
+ for (final StepHandler handler : integrator.getStepHandlers()) {
+ if (handler instanceof StepHandlerWrapper) {
+ handlers.add(((StepHandlerWrapper) handler).getHandler());
+ }
+ }
+ return handlers;
+ }
+
+ /** Remove all the step handlers that have been added to the integrator.
+ * @see #addStepHandler(StepHandlerWithJacobians)
+ * @see #getStepHandlers()
+ */
+ public void clearStepHandlers() {
+ integrator.clearStepHandlers();
+ }
+
+ /** 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
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ public void addEventHandler(EventHandlerWithJacobians handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount) {
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ integrator.addEventHandler(new EventHandlerWrapper(handler, n, k),
+ maxCheckInterval, convergence, maxIterationCount);
+ }
+
+ /** Get all the event handlers that have been added to the integrator.
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
+ * @see #clearEventHandlers()
+ */
+ public Collection<EventHandlerWithJacobians> getEventHandlers() {
+ final Collection<EventHandlerWithJacobians> handlers =
+ new ArrayList<EventHandlerWithJacobians>();
+ for (final EventHandler handler : integrator.getEventHandlers()) {
+ if (handler instanceof EventHandlerWrapper) {
+ handlers.add(((EventHandlerWrapper) handler).getHandler());
+ }
+ }
+ return handlers;
+ }
+
+ /** Remove all the event handlers that have been added to the integrator.
+ * @see #addEventHandler(EventHandlerWithJacobians, double, double, int)
+ * @see #getEventHandlers()
+ */
+ public void clearEventHandlers() {
+ integrator.clearEventHandlers();
+ }
+
+ /** Integrate the differential equations and the variational equations up to the given time.
+ * <p>This method solves an Initial Value Problem (IVP) and also computes the derivatives
+ * of the solution with respect to initial state and parameters. This can be used as
+ * a basis to solve Boundary Value Problems (BVP).</p>
+ * <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.</p>
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param dY0dP initial value of the state vector derivative with respect to the
+ * parameters 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
+ * @param dYdY0 placeholder where to put the state vector derivative with respect
+ * to the initial state (dy[i]/dy0[j] is in element array dYdY0[i][j]) at each successful
+ * step (and hence at the end of integration)
+ * @param dYdP placeholder where to put the state vector derivative with respect
+ * to the parameters (dy[i]/dp[j] is in element array dYdP[i][j]) at each successful
+ * step (and hence at the end of integration)
+ * @return stop time, will be the same as target time if integration reached its
+ * target, but may be different if some event handler stops it at some point.
+ * @throws IntegratorException if the integrator cannot perform integration
+ * @throws DerivativeException this exception is propagated to the caller if
+ * the underlying user function triggers one
+ */
+ public double integrate(final double t0, final double[] y0, final double[][] dY0dP,
+ final double t, final double[] y,
+ final double[][] dYdY0, final double[][] dYdP)
+ throws DerivativeException, IntegratorException {
+
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ checkDimension(n, y0);
+ checkDimension(n, y);
+ checkDimension(n, dYdY0);
+ checkDimension(n, dYdY0[0]);
+ if (k != 0) {
+ checkDimension(n, dY0dP);
+ checkDimension(k, dY0dP[0]);
+ checkDimension(n, dYdP);
+ checkDimension(k, dYdP[0]);
+ }
+
+ // set up initial state, including partial derivatives
+ // the compound state z contains the raw state y and its derivatives
+ // with respect to initial state y0 and to parameters p
+ // y[i] is stored in z[i]
+ // dy[i]/dy0[j] is stored in z[n + i * n + j]
+ // dy[i]/dp[j] is stored in z[n * (n + 1) + i * k + j]
+ final double[] z = new double[n * (1 + n + k)];
+ System.arraycopy(y0, 0, z, 0, n);
+ for (int i = 0; i < n; ++i) {
+
+ // set diagonal element of dy/dy0 to 1.0 at t = t0
+ z[i * (1 + n) + n] = 1.0;
+
+ // set initial derivatives with respect to parameters
+ System.arraycopy(dY0dP[i], 0, z, n * (n + 1) + i * k, k);
+
+ }
+
+ // integrate the compound state variational equations
+ evaluations = 0;
+ final double stopTime = integrator.integrate(new MappingWrapper(), t0, z, t, z);
+
+ // dispatch the final compound state into the state and partial derivatives arrays
+ dispatchCompoundState(z, y, dYdY0, dYdP);
+
+ return stopTime;
+
+ }
+
+ /** Dispatch a compound state array into state and jacobians arrays.
+ * @param z compound state
+ * @param y raw state array to fill
+ * @param dydy0 jacobian array to fill
+ * @param dydp jacobian array to fill
+ */
+ private static void dispatchCompoundState(final double[] z, final double[] y,
+ final double[][] dydy0, final double[][] dydp) {
+
+ final int n = y.length;
+ final int k = dydp[0].length;
+
+ // state
+ System.arraycopy(z, 0, y, 0, n);
+
+ // jacobian with respect to initial state
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(z, n * (i + 1), dydy0[i], 0, n);
+ }
+
+ // jacobian with respect to parameters
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(z, n * (n + 1) + i * k, dydp[i], 0, k);
+ }
+
+ }
+
+ /** 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 org.apache.commons.math.ode.FirstOrderDifferentialEquations
+ * differential equations} problem) if the value of the current step that
+ * is attempted is needed.</p>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @return current value of the step start time t<sub>i</sub>
+ */
+ public double getCurrentStepStart() {
+ return integrator.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 org.apache.commons.math.ode.FirstOrderDifferentialEquations
+ * differential equations} problem) if the signed value of the current stepsize
+ * that is tried is needed.</p>
+ * <p>The result is undefined if the method is called outside of
+ * calls to <code>integrate</code>.</p>
+ * @return current signed value of the stepsize
+ */
+ public double getCurrentSignedStepsize() {
+ return integrator.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.</p>
+ * @param maxEvaluations maximal number of function evaluations (negative
+ * values are silently converted to maximal integer value, thus representing
+ * almost unlimited evaluations)
+ */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations;
+ }
+
+ /** Get the maximal number of functions evaluations.
+ * @return maximal number of functions evaluations
+ */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** 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.
+ * </p>
+ * @return number of evaluations of the differential equations function
+ */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** Check array dimensions.
+ * @param expected expected dimension
+ * @param array (may be null if expected is 0)
+ * @throws IllegalArgumentException if the array dimension does not match the expected one
+ */
+ private void checkDimension(final int expected, final Object array)
+ throws IllegalArgumentException {
+ int arrayDimension = (array == null) ? 0 : Array.getLength(array);
+ if (arrayDimension != expected) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, arrayDimension, expected);
+ }
+ }
+
+ /** Wrapper class used to map state and jacobians into compound state. */
+ private class MappingWrapper implements ExtendedFirstOrderDifferentialEquations {
+
+ /** Current state. */
+ private final double[] y;
+
+ /** Time derivative of the current state. */
+ private final double[] yDot;
+
+ /** Derivatives of yDot with respect to state. */
+ private final double[][] dFdY;
+
+ /** Derivatives of yDot with respect to parameters. */
+ private final double[][] dFdP;
+
+ /** Simple constructor.
+ */
+ public MappingWrapper() {
+
+ final int n = ode.getDimension();
+ final int k = ode.getParametersDimension();
+ y = new double[n];
+ yDot = new double[n];
+ dFdY = new double[n][n];
+ dFdP = new double[n][k];
+
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ final int n = y.length;
+ final int k = dFdP[0].length;
+ return n * (1 + n + k);
+ }
+
+ /** {@inheritDoc} */
+ public int getMainSetDimension() {
+ return ode.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(final double t, final double[] z, final double[] zDot)
+ throws DerivativeException {
+
+ final int n = y.length;
+ final int k = dFdP[0].length;
+
+ // compute raw ODE and its jacobians: dy/dt, d[dy/dt]/dy0 and d[dy/dt]/dp
+ System.arraycopy(z, 0, y, 0, n);
+ if (++evaluations > maxEvaluations) {
+ throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
+ }
+ ode.computeDerivatives(t, y, yDot);
+ ode.computeJacobians(t, y, yDot, dFdY, dFdP);
+
+ // state part of the compound equations
+ System.arraycopy(yDot, 0, zDot, 0, n);
+
+ // variational equations: from d[dy/dt]/dy0 to d[dy/dy0]/dt
+ for (int i = 0; i < n; ++i) {
+ final double[] dFdYi = dFdY[i];
+ for (int j = 0; j < n; ++j) {
+ double s = 0;
+ final int startIndex = n + j;
+ int zIndex = startIndex;
+ for (int l = 0; l < n; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex += n;
+ }
+ zDot[startIndex + i * n] = s;
+ }
+ }
+
+ // variational equations: from d[dy/dt]/dy0 and d[dy/dt]/dp to d[dy/dp]/dt
+ for (int i = 0; i < n; ++i) {
+ final double[] dFdYi = dFdY[i];
+ final double[] dFdPi = dFdP[i];
+ for (int j = 0; j < k; ++j) {
+ double s = dFdPi[j];
+ final int startIndex = n * (n + 1) + j;
+ int zIndex = startIndex;
+ for (int l = 0; l < n; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex += k;
+ }
+ zDot[startIndex + i * k] = s;
+ }
+ }
+
+ }
+
+ }
+
+ /** Wrapper class to compute jacobians by finite differences for ODE which do not compute them themselves. */
+ private class FiniteDifferencesWrapper implements ODEWithJacobians {
+
+ /** Raw ODE without jacobians computation. */
+ private final ParameterizedODE ode;
+
+ /** Parameters array (may be null if parameters dimension from original problem is zero) */
+ private final double[] p;
+
+ /** Step sizes to use for computing the jacobian df/dy. */
+ private final double[] hY;
+
+ /** Step sizes to use for computing the jacobian df/dp. */
+ private final double[] hP;
+
+ /** Temporary array for state derivatives used to compute jacobians. */
+ private final double[] tmpDot;
+
+ /** Simple constructor.
+ * @param ode original ODE problem, without jacobians computations
+ * @param p parameters array (may be null if parameters dimension from original problem is zero)
+ * @param hY step sizes to use for computing the jacobian df/dy
+ * @param hP step sizes to use for computing the jacobian df/dp
+ */
+ public FiniteDifferencesWrapper(final ParameterizedODE ode,
+ final double[] p, final double[] hY, final double[] hP) {
+ this.ode = ode;
+ this.p = p.clone();
+ this.hY = hY.clone();
+ this.hP = hP.clone();
+ tmpDot = new double[ode.getDimension()];
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return ode.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(double t, double[] y, double[] yDot) throws DerivativeException {
+ // this call to computeDerivatives has already been counted,
+ // we must not increment the counter again
+ ode.computeDerivatives(t, y, yDot);
+ }
+
+ /** {@inheritDoc} */
+ public int getParametersDimension() {
+ return ode.getParametersDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeJacobians(double t, double[] y, double[] yDot,
+ double[][] dFdY, double[][] dFdP)
+ throws DerivativeException {
+
+ final int n = hY.length;
+ final int k = hP.length;
+
+ evaluations += n + k;
+ if (evaluations > maxEvaluations) {
+ throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
+ }
+
+ // compute df/dy where f is the ODE and y is the state array
+ 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;
+ }
+
+ // compute df/dp where f is the ODE and p is the parameters array
+ for (int j = 0; j < k; ++j) {
+ ode.setParameter(j, p[j] + hP[j]);
+ ode.computeDerivatives(t, y, tmpDot);
+ for (int i = 0; i < n; ++i) {
+ dFdP[i][j] = (tmpDot[i] - yDot[i]) / hP[j];
+ }
+ ode.setParameter(j, p[j]);
+ }
+
+ }
+
+ }
+
+ /** Wrapper for step handlers. */
+ private static class StepHandlerWrapper implements StepHandler {
+
+ /** Underlying step handler with jacobians. */
+ private final StepHandlerWithJacobians handler;
+
+ /** Dimension of the original ODE. */
+ private final int n;
+
+ /** Number of parameters. */
+ private final int k;
+
+ /** Simple constructor.
+ * @param handler underlying step handler with jacobians
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public StepHandlerWrapper(final StepHandlerWithJacobians handler,
+ final int n, final int k) {
+ this.handler = handler;
+ this.n = n;
+ this.k = k;
+ }
+
+ /** Get the underlying step handler with jacobians.
+ * @return underlying step handler with jacobians
+ */
+ public StepHandlerWithJacobians getHandler() {
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ handler.handleStep(new StepInterpolatorWrapper(interpolator, n, k), isLast);
+ }
+
+ /** {@inheritDoc} */
+ public boolean requiresDenseOutput() {
+ return handler.requiresDenseOutput();
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ handler.reset();
+ }
+
+ }
+
+ /** Wrapper for step interpolators. */
+ private static class StepInterpolatorWrapper
+ implements StepInterpolatorWithJacobians {
+
+ /** Wrapped interpolator. */
+ private StepInterpolator interpolator;
+
+ /** State array. */
+ private double[] y;
+
+ /** Jacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0;
+
+ /** Jacobian with respect to parameters dy/dp. */
+ private double[][] dydp;
+
+ /** Time derivative of the state array. */
+ private double[] yDot;
+
+ /** Time derivative of the sacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0Dot;
+
+ /** Time derivative of the jacobian with respect to parameters dy/dp. */
+ private double[][] dydpDot;
+
+ /** Simple constructor.
+ * <p>This constructor is used only for externalization. It does nothing.</p>
+ */
+ @SuppressWarnings("unused")
+ public StepInterpolatorWrapper() {
+ }
+
+ /** Simple constructor.
+ * @param interpolator wrapped interpolator
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public StepInterpolatorWrapper(final StepInterpolator interpolator,
+ final int n, final int k) {
+ this.interpolator = interpolator;
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ yDot = new double[n];
+ dydy0Dot = new double[n][n];
+ dydpDot = new double[n][k];
+ }
+
+ /** {@inheritDoc} */
+ public void setInterpolatedTime(double time) {
+ interpolator.setInterpolatedTime(time);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isForward() {
+ return interpolator.isForward();
+ }
+
+ /** {@inheritDoc} */
+ public double getPreviousTime() {
+ return interpolator.getPreviousTime();
+ }
+
+ /** {@inheritDoc} */
+ public double getInterpolatedTime() {
+ return interpolator.getInterpolatedTime();
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedY() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ System.arraycopy(extendedState, 0, y, 0, y.length);
+ return y;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDy0() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ final int n = y.length;
+ int start = n;
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedState, start, dydy0[i], 0, n);
+ start += n;
+ }
+ return dydy0;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDp() throws DerivativeException {
+ double[] extendedState = interpolator.getInterpolatedState();
+ final int n = y.length;
+ final int k = dydp[0].length;
+ int start = n * (n + 1);
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedState, start, dydp[i], 0, k);
+ start += k;
+ }
+ return dydp;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedYDot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ System.arraycopy(extendedDerivatives, 0, yDot, 0, yDot.length);
+ return yDot;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDy0Dot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ final int n = y.length;
+ int start = n;
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedDerivatives, start, dydy0Dot[i], 0, n);
+ start += n;
+ }
+ return dydy0Dot;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getInterpolatedDyDpDot() throws DerivativeException {
+ double[] extendedDerivatives = interpolator.getInterpolatedDerivatives();
+ final int n = y.length;
+ final int k = dydpDot[0].length;
+ int start = n * (n + 1);
+ for (int i = 0; i < n; ++i) {
+ System.arraycopy(extendedDerivatives, start, dydpDot[i], 0, k);
+ start += k;
+ }
+ return dydpDot;
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentTime() {
+ return interpolator.getCurrentTime();
+ }
+
+ /** {@inheritDoc} */
+ public StepInterpolatorWithJacobians copy() throws DerivativeException {
+ final int n = y.length;
+ final int k = dydp[0].length;
+ StepInterpolatorWrapper copied =
+ new StepInterpolatorWrapper(interpolator.copy(), n, k);
+ copyArray(y, copied.y);
+ copyArray(dydy0, copied.dydy0);
+ copyArray(dydp, copied.dydp);
+ copyArray(yDot, copied.yDot);
+ copyArray(dydy0Dot, copied.dydy0Dot);
+ copyArray(dydpDot, copied.dydpDot);
+ return copied;
+ }
+
+ /** {@inheritDoc} */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(interpolator);
+ out.writeInt(y.length);
+ out.writeInt(dydp[0].length);
+ writeArray(out, y);
+ writeArray(out, dydy0);
+ writeArray(out, dydp);
+ writeArray(out, yDot);
+ writeArray(out, dydy0Dot);
+ writeArray(out, dydpDot);
+ }
+
+ /** {@inheritDoc} */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ interpolator = (StepInterpolator) in.readObject();
+ final int n = in.readInt();
+ final int k = in.readInt();
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ yDot = new double[n];
+ dydy0Dot = new double[n][n];
+ dydpDot = new double[n][k];
+ readArray(in, y);
+ readArray(in, dydy0);
+ readArray(in, dydp);
+ readArray(in, yDot);
+ readArray(in, dydy0Dot);
+ readArray(in, dydpDot);
+ }
+
+ /** Copy an array.
+ * @param src source array
+ * @param dest destination array
+ */
+ private static void copyArray(final double[] src, final double[] dest) {
+ System.arraycopy(src, 0, dest, 0, src.length);
+ }
+
+ /** Copy an array.
+ * @param src source array
+ * @param dest destination array
+ */
+ private static void copyArray(final double[][] src, final double[][] dest) {
+ for (int i = 0; i < src.length; ++i) {
+ copyArray(src[i], dest[i]);
+ }
+ }
+
+ /** Write an array.
+ * @param out output stream
+ * @param array array to write
+ * @exception IOException if array cannot be read
+ */
+ private static void writeArray(final ObjectOutput out, final double[] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ out.writeDouble(array[i]);
+ }
+ }
+
+ /** Write an array.
+ * @param out output stream
+ * @param array array to write
+ * @exception IOException if array cannot be read
+ */
+ private static void writeArray(final ObjectOutput out, final double[][] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ writeArray(out, array[i]);
+ }
+ }
+
+ /** Read an array.
+ * @param in input stream
+ * @param array array to read
+ * @exception IOException if array cannot be read
+ */
+ private static void readArray(final ObjectInput in, final double[] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = in.readDouble();
+ }
+ }
+
+ /** Read an array.
+ * @param in input stream
+ * @param array array to read
+ * @exception IOException if array cannot be read
+ */
+ private static void readArray(final ObjectInput in, final double[][] array)
+ throws IOException {
+ for (int i = 0; i < array.length; ++i) {
+ readArray(in, array[i]);
+ }
+ }
+
+ }
+
+ /** Wrapper for event handlers. */
+ private static class EventHandlerWrapper implements EventHandler {
+
+ /** Underlying event handler with jacobians. */
+ private final EventHandlerWithJacobians handler;
+
+ /** State array. */
+ private double[] y;
+
+ /** Jacobian with respect to initial state dy/dy0. */
+ private double[][] dydy0;
+
+ /** Jacobian with respect to parameters dy/dp. */
+ private double[][] dydp;
+
+ /** Simple constructor.
+ * @param handler underlying event handler with jacobians
+ * @param n dimension of the original ODE
+ * @param k number of parameters
+ */
+ public EventHandlerWrapper(final EventHandlerWithJacobians handler,
+ final int n, final int k) {
+ this.handler = handler;
+ y = new double[n];
+ dydy0 = new double[n][n];
+ dydp = new double[n][k];
+ }
+
+ /** Get the underlying event handler with jacobians.
+ * @return underlying event handler with jacobians
+ */
+ public EventHandlerWithJacobians getHandler() {
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public int eventOccurred(double t, double[] z, boolean increasing)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ return handler.eventOccurred(t, y, dydy0, dydp, increasing);
+ }
+
+ /** {@inheritDoc} */
+ public double g(double t, double[] z)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ return handler.g(t, y, dydy0, dydp);
+ }
+
+ /** {@inheritDoc} */
+ public void resetState(double t, double[] z)
+ throws EventException {
+ dispatchCompoundState(z, y, dydy0, dydp);
+ handler.resetState(t, y, dydy0, dydp);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/ODEWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/ODEWithJacobians.java
new file mode 100644
index 0000000..40a7e77
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/ODEWithJacobians.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.math.ode.jacobians;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+
+
+/** This interface represents {@link ParameterizedODE
+ * first order differential equations} with parameters and partial derivatives.
+ *
+ * @see FirstOrderIntegratorWithJacobians
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public interface ODEWithJacobians extends FirstOrderDifferentialEquations {
+
+ /** Get the number of parameters.
+ * @return number of parameters
+ */
+ int getParametersDimension();
+
+ /** Compute the partial derivatives of ODE with respect to state.
+ * @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 time derivative of the state vector
+ * @param dFdY placeholder array where to put the jacobian of the ODE with respect to the state vector
+ * @param dFdP placeholder array where to put the jacobian of the ODE with respect to the parameters
+ * @throws DerivativeException this exception is propagated to the caller if the
+ * underlying user function triggers one
+ */
+ void computeJacobians(double t, double[] y, double[] yDot, double[][] dFdY, double[][] dFdP)
+ throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/ParameterizedODE.java b/src/main/java/org/apache/commons/math/ode/jacobians/ParameterizedODE.java
new file mode 100644
index 0000000..89caad7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/ParameterizedODE.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.math.ode.jacobians;
+
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+
+
+/** This interface represents {@link FirstOrderDifferentialEquations
+ * first order differential equations} with parameters.
+ *
+ * @see FirstOrderIntegratorWithJacobians
+ *
+ * @version $Revision: 1037341 $ $Date: 2010-11-20 22:58:35 +0100 (sam. 20 nov. 2010) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public interface ParameterizedODE extends FirstOrderDifferentialEquations {
+
+ /** Get the number of parameters.
+ * @return number of parameters
+ */
+ int getParametersDimension();
+
+ /** Set a parameter.
+ * @param i index of the parameters (must be between 0
+ * and {@link #getParametersDimension() getParametersDimension() - 1})
+ * @param value value for the parameter
+ */
+ void setParameter(int i, double value);
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/StepHandlerWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/StepHandlerWithJacobians.java
new file mode 100644
index 0000000..8cbb747
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/StepHandlerWithJacobians.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.math.ode.jacobians;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/**
+ * 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>
+ *
+ * <p>Note that is is possible to register a {@link
+ * org.apache.commons.math.ode.sampling.StepHandler classical step handler}
+ * in the low level integrator used to build a {@link FirstOrderIntegratorWithJacobians}
+ * rather than implementing this class. The step handlers registered at low level
+ * will see the big compound state whether the step handlers defined by this interface
+ * see the original state, and its jacobians in separate arrays.</p>
+ *
+ * <p>The compound state is guaranteed to contain the original state in the first
+ * elements, followed by the jacobian with respect to initial state (in row order),
+ * followed by the jacobian with respect to parameters (in row order). If for example
+ * the original state dimension is 6 and there are 3 parameters, the compound state will
+ * be a 60 elements array. The first 6 elements will be the original state, the next 36
+ * elements will be the jacobian with respect to initial state, and the remaining 18 elements
+ * will be the jacobian with respect to parameters.</p>
+ *
+ * <p>Dealing with low level step handlers is cumbersome if one really needs the jacobians
+ * in these methods, but it also prevents many data being copied back and forth between
+ * state and jacobians on one side and compound state on the other side. So for performance
+ * reasons, it is recommended to use this interface <em>only</em> if jacobians are really
+ * needed and to use lower level handlers if only state is needed.</p>
+ *
+ * @see FirstOrderIntegratorWithJacobians
+ * @see StepInterpolatorWithJacobians
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public interface StepHandlerWithJacobians {
+
+ /** Determines whether this handler needs dense output.
+ * <p>This method allows the integrator to avoid performing extra
+ * computation if the handler does not need dense output.</p>
+ * @return true if the handler needs dense output
+ */
+ boolean requiresDenseOutput();
+
+ /** Reset the step handler.
+ * Initialize the internal data as required before the first step is
+ * handled.
+ */
+ void reset();
+
+ /**
+ * 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.math.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
+ * @throws DerivativeException this exception is propagated to the
+ * caller if the underlying user function triggers one
+ */
+ void handleStep(StepInterpolatorWithJacobians interpolator, boolean isLast) throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/StepInterpolatorWithJacobians.java b/src/main/java/org/apache/commons/math/ode/jacobians/StepInterpolatorWithJacobians.java
new file mode 100644
index 0000000..bff9d17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/StepInterpolatorWithJacobians.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.jacobians;
+
+import java.io.Externalizable;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/** 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 FirstOrderIntegratorWithJacobians
+ * @see StepHandlerWithJacobians
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.1
+ * @deprecated as of 2.2 the complete package is deprecated, it will be replaced
+ * in 3.0 by a completely rewritten implementation
+ */
+@Deprecated
+public interface StepInterpolatorWithJacobians 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. If a
+ * specific state must be preserved, a copy of the instance must be
+ * created using {@link #copy()}.</p>
+ * @param time time of the interpolated point
+ */
+ 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.</p>
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedYDot()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[] getInterpolatedY() throws DerivativeException;
+
+ /**
+ * Get the partial derivatives of the state vector with respect to
+ * the initial state 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.</p>
+ * @return partial derivatives of the state vector with respect to
+ * the initial state at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedY()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[][] getInterpolatedDyDy0() throws DerivativeException;
+
+ /**
+ * Get the partial derivatives of the state vector with respect to
+ * the ODE parameters 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.</p>
+ * @return partial derivatives of the state vector with respect to
+ * the ODE parameters at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedY()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[][] getInterpolatedDyDp() throws DerivativeException;
+
+ /**
+ * Get the time 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.</p>
+ * @return derivatives of the state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedY()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[] getInterpolatedYDot() throws DerivativeException;
+
+ /**
+ * Get the time derivatives of the jacobian of the state vector
+ * with respect to the initial state 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.</p>
+ * @return time derivatives of the jacobian of the state vector
+ * with respect to the initial state at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedY()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[][] getInterpolatedDyDy0Dot() throws DerivativeException;
+
+ /**
+ * Get the time derivatives of the jacobian of the state vector
+ * with respect to the ODE parameters 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.</p>
+ * @return time derivatives of the jacobian of the state vector
+ * with respect to the ODE parameters at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedY()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ double[][] getInterpolatedDyDpDot() throws DerivativeException;
+
+ /** 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.
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ * @see #setInterpolatedTime(double)
+ */
+ StepInterpolatorWithJacobians copy() throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/jacobians/package.html b/src/main/java/org/apache/commons/math/ode/jacobians/package.html
new file mode 100644
index 0000000..29d6f8f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/jacobians/package.html
@@ -0,0 +1,28 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 1037341 $ -->
+<body>
+<p>
+This package was intended to solve Ordinary Differential Equations problems
+and also compute derivatives of the solution. It was introduced in 2.1 but is
+difficult to use and clumsy. It is completely deprecated in 2.2 and will be removed
+in 3.0, to be replaced by a completely new implementation, much more tightly
+bound to the top level ode package.
+</p>
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
new file mode 100644
index 0000000..6ba7733
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.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(k)<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</sub> j (-i)<sup>j-1</sup> s<sub>j</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 (-i)<sup>j-1</sup> terms:
+ * <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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 (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 IllegalArgumentException if order is 1 or less
+ */
+ public AdamsBashforthIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws IllegalArgumentException {
+ 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 (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
+ * @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);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws DerivativeException, IntegratorException {
+
+ final int n = y0.length;
+ sanityChecks(equations, t0, y0, t, y);
+ setEquations(equations);
+ resetEvaluations();
+ final boolean forward = t > t0;
+
+ // initialize working arrays
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, n);
+ }
+ final double[] yDot = new double[n];
+
+ // set up an interpolator sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward);
+
+ // set up integration control objects
+ for (StepHandler handler : stepHandlers) {
+ handler.reset();
+ }
+ setStateInitialized(false);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(t0, y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ interpolator.storeTime(stepStart);
+ final int lastRow = nordsieck.getRowDimension() - 1;
+
+ // reuse the step that was chosen by the starter integrator
+ double hNew = stepSize;
+ interpolator.rescale(hNew);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ double error = 10;
+ while (error >= 1.0) {
+
+ stepSize = hNew;
+
+ // evaluate error using the last term of the Taylor expansion
+ error = 0;
+ for (int i = 0; i < mainSetDimension; ++i) {
+ final double yScale = FastMath.abs(y[i]);
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yScale);
+ final double ratio = nordsieck.getEntry(lastRow, i) / tol;
+ error += ratio * ratio;
+ }
+ error = FastMath.sqrt(error / mainSetDimension);
+
+ 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);
+
+ }
+ }
+
+ // predict a first estimate of the state at step end
+ final double stepEnd = stepStart + stepSize;
+ interpolator.shift();
+ interpolator.setInterpolatedTime(stepEnd);
+ System.arraycopy(interpolator.getInterpolatedState(), 0, y, 0, y0.length);
+
+ // evaluate the derivative
+ computeDerivatives(stepEnd, y, yDot);
+
+ // update Nordsieck vector
+ final double[] predictedScaled = new double[y0.length];
+ for (int j = 0; j < y0.length; ++j) {
+ predictedScaled[j] = stepSize * yDot[j];
+ }
+ final Array2DRowRealMatrix nordsieckTmp = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, nordsieckTmp);
+ interpolator.reinitialize(stepEnd, stepSize, predictedScaled, nordsieckTmp);
+
+ // discrete events handling
+ interpolator.storeTime(stepEnd);
+ stepStart = acceptStep(interpolator, y, yDot, t);
+ scaled = predictedScaled;
+ nordsieck = nordsieckTmp;
+ 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);
+
+ final double stopTime = stepStart;
+ resetInternalState();
+ return stopTime;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsIntegrator.java
new file mode 100644
index 0000000..0b114f0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsIntegrator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.MultistepIntegrator;
+
+
+/** Base class for {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators.
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 prameters.
+ * @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 (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 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 scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws IllegalArgumentException {
+ 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 (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
+ * @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 double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws DerivativeException, IntegratorException;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Array2DRowRealMatrix initializeHighOrderDerivatives(final double[] first,
+ final double[][] multistep) {
+ return transformer.initializeHighOrderDerivatives(first, multistep);
+ }
+
+ /** 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/math/ode/nonstiff/AdamsMoultonIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java
new file mode 100644
index 0000000..77a4418
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.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.math.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.RealMatrixPreservingVisitor;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.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(k)<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</sub> j (-i)<sup>j-1</sup> s<sub>j</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 (-i)<sup>j-1</sup> terms:
+ * <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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 (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 IllegalArgumentException if order is 1 or less
+ */
+ public AdamsMoultonIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws IllegalArgumentException {
+ 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 (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
+ * @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 double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws DerivativeException, IntegratorException {
+
+ final int n = y0.length;
+ sanityChecks(equations, t0, y0, t, y);
+ setEquations(equations);
+ resetEvaluations();
+ final boolean forward = t > t0;
+
+ // initialize working arrays
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, n);
+ }
+ final double[] yDot = new double[y0.length];
+ final double[] yTmp = new double[y0.length];
+ final double[] predictedScaled = new double[y0.length];
+ Array2DRowRealMatrix nordsieckTmp = null;
+
+ // set up two interpolators sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward);
+
+ // set up integration control objects
+ for (StepHandler handler : stepHandlers) {
+ handler.reset();
+ }
+ setStateInitialized(false);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(t0, 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);
+ System.arraycopy(interpolator.getInterpolatedState(), 0, yTmp, 0, y0.length);
+
+ // 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, n);
+ 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);
+
+ final double stopTime = stepStart;
+ stepStart = Double.NaN;
+ stepSize = Double.NaN;
+ return stopTime;
+
+ }
+
+ /** 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)
+ */
+ public 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;
+ error += ratio * ratio;
+ }
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsNordsieckTransformer.java b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsNordsieckTransformer.java
new file mode 100644
index 0000000..1f16a76
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsNordsieckTransformer.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math.fraction.BigFraction;
+import org.apache.commons.math.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.DefaultFieldMatrixChangingVisitor;
+import org.apache.commons.math.linear.FieldDecompositionSolver;
+import org.apache.commons.math.linear.FieldLUDecompositionImpl;
+import org.apache.commons.math.linear.FieldMatrix;
+import org.apache.commons.math.linear.MatrixUtils;
+
+/** Transformer to Nordsieck vectors for Adams integrators.
+ * <p>This class i 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(k)<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</sub> j (-i)<sup>j-1</sup> s<sub>j</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 (-i)<sup>j-1</sup> terms:
+ * <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>
+ *
+ * @version $Revision: 810196 $ $Date: 2009-09-01 21:47:46 +0200 (mar. 01 sept. 2009) $
+ * @since 2.0
+ */
+public class AdamsNordsieckTransformer {
+
+ /** Cache for already computed coefficients. */
+ private static final Map<Integer, AdamsNordsieckTransformer> CACHE =
+ new HashMap<Integer, AdamsNordsieckTransformer>();
+
+ /** Initialization matrix for the higher order derivatives wrt y'', y''' ... */
+ private final Array2DRowRealMatrix initialization;
+
+ /** Update matrix for the higher order derivatives h<sup>2</sup>/2y'', 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 nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ */
+ private AdamsNordsieckTransformer(final int nSteps) {
+
+ // compute exact coefficients
+ FieldMatrix<BigFraction> bigP = buildP(nSteps);
+ FieldDecompositionSolver<BigFraction> pSolver =
+ new FieldLUDecompositionImpl<BigFraction>(bigP).getSolver();
+
+ BigFraction[] u = new BigFraction[nSteps];
+ Arrays.fill(u, BigFraction.ONE);
+ BigFraction[] bigC1 = pSolver.solve(u);
+
+ // 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[nSteps];
+ Arrays.fill(shiftedP[0], BigFraction.ZERO);
+ FieldMatrix<BigFraction> bigMSupdate =
+ pSolver.solve(new Array2DRowFieldMatrix<BigFraction>(shiftedP, false));
+
+ // initialization coefficients, computed from a R matrix = abs(P)
+ bigP.walkInOptimizedOrder(new DefaultFieldMatrixChangingVisitor<BigFraction>(BigFraction.ZERO) {
+ /** {@inheritDoc} */
+ @Override
+ public BigFraction visit(int row, int column, BigFraction value) {
+ return ((column & 0x1) == 0x1) ? value : value.negate();
+ }
+ });
+ FieldMatrix<BigFraction> bigRInverse =
+ new FieldLUDecompositionImpl<BigFraction>(bigP).getSolver().getInverse();
+
+ // convert coefficients to double
+ initialization = MatrixUtils.bigFractionMatrixToRealMatrix(bigRInverse);
+ update = MatrixUtils.bigFractionMatrixToRealMatrix(bigMSupdate);
+ c1 = new double[nSteps];
+ for (int i = 0; i < nSteps; ++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)
+ */
+ public int getNSteps() {
+ return c1.length;
+ }
+
+ /** Build the P matrix.
+ * <p>The P matrix general terms are shifted j (-i)<sup>j-1</sup> terms:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ * @param nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ * @return P matrix
+ */
+ private FieldMatrix<BigFraction> buildP(final int nSteps) {
+
+ final BigFraction[][] pData = new BigFraction[nSteps][nSteps];
+
+ for (int i = 0; i < pData.length; ++i) {
+ // build the P matrix elements from Taylor series formulas
+ final BigFraction[] pI = pData[i];
+ final int factor = -(i + 1);
+ int aj = factor;
+ for (int j = 0; j < pI.length; ++j) {
+ pI[j] = new BigFraction(aj * (j + 2));
+ aj *= factor;
+ }
+ }
+
+ return new Array2DRowFieldMatrix<BigFraction>(pData, false);
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param first first scaled derivative at step start
+ * @param multistep scaled derivatives after step start (hy'1, ..., hy'k-1)
+ * will be modified
+ * @return high order derivatives at step start
+ */
+ public Array2DRowRealMatrix initializeHighOrderDerivatives(final double[] first,
+ final double[][] multistep) {
+ for (int i = 0; i < multistep.length; ++i) {
+ final double[] msI = multistep[i];
+ for (int j = 0; j < first.length; ++j) {
+ msI[j] -= first[j];
+ }
+ }
+ return initialization.multiply(new Array2DRowRealMatrix(multistep, false));
+ }
+
+ /** 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/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.java
new file mode 100644
index 0000000..bfae8f4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/AdaptiveStepsizeIntegrator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.AbstractIntegrator;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ExtendedFirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.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 ExtendedFirstOrderDifferentialEquations
+ * extended ODE} rather than a {@link FirstOrderDifferentialEquations basic ODE},
+ * then <em>only</em> the {@link ExtendedFirstOrderDifferentialEquations#getMainSetDimension()
+ * main set} 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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ *
+ */
+
+public abstract class AdaptiveStepsizeIntegrator
+ extends AbstractIntegrator {
+
+ /** Allowed absolute scalar error. */
+ protected final double scalAbsoluteTolerance;
+
+ /** Allowed relative scalar error. */
+ protected final double scalRelativeTolerance;
+
+ /** Allowed absolute vectorial error. */
+ protected final double[] vecAbsoluteTolerance;
+
+ /** Allowed relative vectorial error. */
+ protected final double[] vecRelativeTolerance;
+
+ /** Main set dimension. */
+ protected int mainSetDimension;
+
+ /** User supplied initial step. */
+ private double initialStep;
+
+ /** Minimal step. */
+ private final double minStep;
+
+ /** Maximal step. */
+ private final 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 (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
+ */
+ public AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name);
+
+ this.minStep = FastMath.abs(minStep);
+ this.maxStep = FastMath.abs(maxStep);
+ this.initialStep = -1.0;
+
+ this.scalAbsoluteTolerance = scalAbsoluteTolerance;
+ this.scalRelativeTolerance = scalRelativeTolerance;
+ this.vecAbsoluteTolerance = null;
+ this.vecRelativeTolerance = null;
+
+ 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 (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 AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(name);
+
+ this.minStep = minStep;
+ this.maxStep = maxStep;
+ this.initialStep = -1.0;
+
+ this.scalAbsoluteTolerance = 0;
+ this.scalRelativeTolerance = 0;
+ this.vecAbsoluteTolerance = vecAbsoluteTolerance.clone();
+ this.vecRelativeTolerance = vecRelativeTolerance.clone();
+
+ resetInternalState();
+
+ }
+
+ /** 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;
+ }
+ }
+
+ /** Perform some sanity checks on the integration parameters.
+ * @param equations differential equations set
+ * @param t0 start time
+ * @param y0 state vector at t0
+ * @param t target time for the integration
+ * @param y placeholder where to put the state vector
+ * @exception IntegratorException if some inconsistency is detected
+ */
+ @Override
+ protected void sanityChecks(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws IntegratorException {
+
+ super.sanityChecks(equations, t0, y0, t, y);
+
+ if (equations instanceof ExtendedFirstOrderDifferentialEquations) {
+ mainSetDimension = ((ExtendedFirstOrderDifferentialEquations) equations).getMainSetDimension();
+ } else {
+ mainSetDimension = equations.getDimension();
+ }
+
+ if ((vecAbsoluteTolerance != null) && (vecAbsoluteTolerance.length != mainSetDimension)) {
+ throw new IntegratorException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, mainSetDimension, vecAbsoluteTolerance.length);
+ }
+
+ if ((vecRelativeTolerance != null) && (vecRelativeTolerance.length != mainSetDimension)) {
+ throw new IntegratorException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, mainSetDimension, vecRelativeTolerance.length);
+ }
+
+ }
+
+ /** Initialize the integration step.
+ * @param equations differential equations set
+ * @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 DerivativeException this exception is propagated to
+ * the caller if the underlying user function triggers one
+ */
+ public double initializeStep(final FirstOrderDifferentialEquations equations,
+ 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 DerivativeException {
+
+ 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 IntegratorException if the step is too small and acceptSmall is false
+ */
+ protected double filterStep(final double h, final boolean forward, final boolean acceptSmall)
+ throws IntegratorException {
+
+ double filteredH = h;
+ if (FastMath.abs(h) < minStep) {
+ if (acceptSmall) {
+ filteredH = forward ? minStep : -minStep;
+ } else {
+ throw new IntegratorException(
+ LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION,
+ minStep, FastMath.abs(h));
+ }
+ }
+
+ if (filteredH > maxStep) {
+ filteredH = maxStep;
+ } else if (filteredH < -maxStep) {
+ filteredH = -maxStep;
+ }
+
+ return filteredH;
+
+ }
+
+ /** {@inheritDoc} */
+ public abstract double integrate (FirstOrderDifferentialEquations equations,
+ double t0, double[] y0,
+ double t, double[] y)
+ throws DerivativeException, IntegratorException;
+
+ /** {@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/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.java
new file mode 100644
index 0000000..a993d40
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.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
+ * @version $Revision: 810196 $ $Date: 2009-09-01 21:47:46 +0200 (mar. 01 sept. 2009) $
+ * @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/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..90596b1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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 :
+
+ * <pre>
+ * y(t_n + theta h) = y (t_n + h)
+ * + (1 - theta) (h/6) [ (-4 theta^2 + 5 theta - 1) y'_1
+ * +(4 theta^2 - 2 theta - 2) (y'_2 + y'_3)
+ * -(4 theta^2 + theta + 1) y'_4
+ * ]
+ * </pre>
+ *
+ * where theta belongs to [0 ; 1] and where y'_1 to y'_4 are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ClassicalRungeKuttaIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class ClassicalRungeKuttaStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -6576285612589783992L;
+
+ /** 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.
+ */
+ public ClassicalRungeKuttaStepInterpolator() {
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ final double fourTheta = 4 * theta;
+ final double oneMinusTheta = 1 - theta;
+ final double oneMinus2Theta = 1 - 2 * 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);
+ final double coeffDot1 = oneMinusTheta * oneMinus2Theta;
+ final double coeffDot23 = 2 * theta * oneMinusTheta;
+ final double coeffDot4 = -theta * oneMinus2Theta;
+ 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/math/ode/nonstiff/DormandPrince54Integrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java
new file mode 100644
index 0000000..e31991f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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 (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
+ */
+ 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 (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 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/math/ode/nonstiff/DormandPrince54StepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.java
new file mode 100644
index 0000000..34cd924
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.AbstractIntegrator;
+import org.apache.commons.math.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
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 = 4104157279605906956L;
+
+ /** 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.
+ */
+ public DormandPrince54StepInterpolator() {
+ super();
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+ }
+
+ /** 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 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) {
+ super.reinitialize(integrator, y, yDotK, forward);
+ 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)
+ throws DerivativeException {
+
+ 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));
+ 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/math/ode/nonstiff/DormandPrince853Integrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.java
new file mode 100644
index 0000000..68c1141
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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 (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
+ */
+ 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 (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 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/math/ode/nonstiff/DormandPrince853StepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java
new file mode 100644
index 0000000..946f8a2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolator.java
@@ -0,0 +1,480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math.ode.AbstractIntegrator;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class DormandPrince853StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 7152276390558450974L;
+
+ /** 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.
+ */
+ public DormandPrince853StepInterpolator() {
+ super();
+ yDotKLast = null;
+ v = null;
+ vectorsInitialized = false;
+ }
+
+ /** 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 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) {
+
+ super.reinitialize(integrator, y, yDotK, forward);
+
+ 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 DerivativeException {
+
+ 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)));
+
+ 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 DerivativeException {
+
+ 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 (DerivativeException e) {
+ IOException ioe = new IOException(e.getLocalizedMessage());
+ ioe.initCause(e);
+ 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 {
+
+ // 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/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
new file mode 100644
index 0000000..97e772e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 (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
+ */
+ 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 double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws DerivativeException, IntegratorException {
+
+ sanityChecks(equations, t0, y0, t, y);
+ setEquations(equations);
+ resetEvaluations();
+ final boolean forward = t > t0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, y0.length);
+ }
+ final double[][] yDotK = new double[stages][y0.length];
+ final double[] yTmp = new double[y0.length];
+ final double[] yDotTmp = new double[y0.length];
+
+ // set up an interpolator sharing the integrator arrays
+ AbstractStepInterpolator interpolator;
+ if (requiresDenseOutput()) {
+ final RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy();
+ rki.reinitialize(this, yTmp, yDotK, forward);
+ interpolator = rki;
+ } else {
+ interpolator = new DummyStepInterpolator(yTmp, yDotK[stages - 1], forward);
+ }
+ interpolator.storeTime(t0);
+
+ // set up integration control objects
+ stepStart = t0;
+ double hNew = 0;
+ boolean firstTime = true;
+ for (StepHandler handler : stepHandlers) {
+ handler.reset();
+ }
+ setStateInitialized(false);
+
+ // 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(equations, forward, getOrder(), scale,
+ stepStart, y, yDotK[0], yTmp, yDotK[1]);
+ firstTime = false;
+ }
+
+ stepSize = hNew;
+
+ // 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);
+
+ 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);
+
+ final double stopTime = stepStart;
+ resetInternalState();
+ return stopTime;
+
+ }
+
+ /** 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/math/ode/nonstiff/EulerIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/EulerIntegrator.java
new file mode 100644
index 0000000..21b0f74
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.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
+ * @version $Revision: 810196 $ $Date: 2009-09-01 21:47:46 +0200 (mar. 01 sept. 2009) $
+ * @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/math/ode/nonstiff/EulerStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.java
new file mode 100644
index 0000000..96f0c13
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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 :
+ *
+ * <pre>
+ * y(t_n + theta h) = y (t_n + h) - (1-theta) h y'
+ * </pre>
+ *
+ * where theta belongs to [0 ; 1] and where y' is the evaluation of
+ * the derivatives already computed during the step.</p>
+ *
+ * @see EulerIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class EulerStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -7179861704951334960L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math.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.
+ */
+ public EulerStepInterpolator() {
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ 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/math/ode/nonstiff/GillIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/GillIntegrator.java
new file mode 100644
index 0000000..3b31f9f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.ode.nonstiff;
+
+import org.apache.commons.math.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
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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/math/ode/nonstiff/GillStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.java
new file mode 100644
index 0000000..3d17d27
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.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 :
+ *
+ * <pre>
+ * y(t_n + theta h) = y (t_n + h)
+ * - (1 - theta) (h/6) [ (1 - theta) (1 - 4 theta) y'_1
+ * + (1 - theta) (1 + 2 theta) ((2-q) y'_2 + (2+q) y'_3)
+ * + (1 + theta + 4 theta^2) y'_4
+ * ]
+ * </pre>
+ * where theta belongs to [0 ; 1], q = sqrt(2) and where y'_1 to y'_4
+ * are the four evaluations of the derivatives already computed during
+ * the step.</p>
+ *
+ * @see GillIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class GillStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** First Gill coefficient. */
+ private static final double TWO_MINUS_SQRT_2 = 2 - FastMath.sqrt(2.0);
+
+ /** Second Gill coefficient. */
+ private static final double TWO_PLUS_SQRT_2 = 2 + FastMath.sqrt(2.0);
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -107804074496313322L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math.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.
+ */
+ public GillStepInterpolator() {
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ final double twoTheta = 2 * theta;
+ final double fourTheta = 4 * theta;
+ final double s = oneMinusThetaH / 6.0;
+ final double oMt = 1 - theta;
+ final double soMt = s * oMt;
+ final double c23 = soMt * (1 + twoTheta);
+ final double coeff1 = soMt * (1 - fourTheta);
+ final double coeff2 = c23 * TWO_MINUS_SQRT_2;
+ final double coeff3 = c23 * TWO_PLUS_SQRT_2;
+ final double coeff4 = s * (1 + theta * (1 + fourTheta));
+ final double coeffDot1 = theta * (twoTheta - 3) + 1;
+ final double cDot23 = theta * oMt;
+ final double coeffDot2 = cDot23 * TWO_MINUS_SQRT_2;
+ final double coeffDot3 = cDot23 * TWO_PLUS_SQRT_2;
+ final double coeffDot4 = theta * (twoTheta - 1);
+
+ 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/math/ode/nonstiff/GraggBulirschStoerIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java
new file mode 100644
index 0000000..09267d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.java
@@ -0,0 +1,963 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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 (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
+ */
+ 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);
+ setStepsizeControl(-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);
+ setStepsizeControl(-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 setStepsizeControl(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) {
+ super.addEventHandler(function, maxCheckInterval, convergence, maxIterationCount);
+
+ // 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];
+ }
+
+ if (requiresDenseOutput()) {
+ // step size sequence: 2, 6, 10, 14, ...
+ for (int k = 0; k < size; ++k) {
+ sequence[k] = 4 * k + 2;
+ }
+ } else {
+ // step size sequence: 2, 4, 6, 8, ...
+ for (int k = 0; k < size; ++k) {
+ sequence[k] = 2 * (k + 1);
+ }
+ }
+
+ // 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
+ * @throws DerivativeException this exception is propagated to the caller if the
+ * underlying user function triggers one
+ */
+ 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 DerivativeException {
+
+ 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 double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0, final double t, final double[] y)
+ throws DerivativeException, IntegratorException {
+
+ sanityChecks(equations, t0, y0, t, y);
+ setEquations(equations);
+ resetEvaluations();
+ final boolean forward = t > t0;
+
+ // create some internal working arrays
+ final double[] yDot0 = new double[y0.length];
+ final double[] y1 = new double[y0.length];
+ final double[] yTmp = new double[y0.length];
+ final double[] yTmpDot = new double[y0.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[y0.length];
+ y1Diag[k] = new double[y0.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);
+ }
+
+ double[] yDot1 = new double[y0.length];
+ double[][] yMidDots = null;
+ final boolean denseOutput = requiresDenseOutput();
+ if (denseOutput) {
+ yMidDots = new double[1 + 2 * sequence.length][];
+ for (int j = 0; j < yMidDots.length; ++j) {
+ yMidDots[j] = new double[y0.length];
+ }
+ } else {
+ yMidDots = new double[1][];
+ yMidDots[0] = new double[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
+ AbstractStepInterpolator interpolator = null;
+ if (denseOutput) {
+ interpolator = new GraggBulirschStoerStepInterpolator(y, yDot0,
+ y1, yDot1,
+ yMidDots, forward);
+ } else {
+ interpolator = new DummyStepInterpolator(y, yDot1, forward);
+ }
+ interpolator.storeTime(t0);
+
+ stepStart = t0;
+ double hNew = 0;
+ double maxError = Double.MAX_VALUE;
+ boolean previousRejected = false;
+ boolean firstTime = true;
+ boolean newStep = true;
+ boolean firstStepAlreadyComputed = false;
+ for (StepHandler handler : stepHandlers) {
+ handler.reset();
+ }
+ setStateInitialized(false);
+ 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(equations, 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 (denseOutput && ! 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);
+
+ final double stopTime = stepStart;
+ resetInternalState();
+ return stopTime;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
new file mode 100644
index 0000000..48fdf2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
@@ -0,0 +1,401 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.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
+ * @version $Revision: 1061507 $ $Date: 2011-01-20 21:55:00 +0100 (jeu. 20 janv. 2011) $
+ * @since 1.2
+ */
+
+class GraggBulirschStoerStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 7320613236731409847L;
+
+ /** 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 polynoms. */
+ private double[][] polynoms;
+
+ /** Error coefficients for the interpolation. */
+ private double[] errfac;
+
+ /** Degree of the interpolation polynoms. */
+ private int currentDegree;
+
+ /** Simple constructor.
+ * This constructor should not be used directly, it is only intended
+ * for the serialization process.
+ */
+ public GraggBulirschStoerStepInterpolator() {
+ y0Dot = null;
+ y1 = null;
+ y1Dot = null;
+ yMidDots = null;
+ resetTables(-1);
+ }
+
+ /** 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
+ */
+ public GraggBulirschStoerStepInterpolator(final double[] y, final double[] y0Dot,
+ final double[] y1, final double[] y1Dot,
+ final double[][] yMidDots,
+ final boolean forward) {
+
+ super(y, forward);
+ 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
+ */
+ public 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 polynoms (up to the current degree only)
+ if (interpolator.polynoms == null) {
+ polynoms = null;
+ currentDegree = -1;
+ } else {
+ resetTables(interpolator.currentDegree);
+ for (int i = 0; i < polynoms.length; ++i) {
+ polynoms[i] = new double[dimension];
+ System.arraycopy(interpolator.polynoms[i], 0,
+ polynoms[i], 0, dimension);
+ }
+ currentDegree = interpolator.currentDegree;
+ }
+
+ }
+
+ /** Reallocate the internal tables.
+ * Reallocate the internal tables in order to be able to handle
+ * interpolation polynoms up to the given degree
+ * @param maxDegree maximal degree to handle
+ */
+ private void resetTables(final int maxDegree) {
+
+ if (maxDegree < 0) {
+ polynoms = null;
+ errfac = null;
+ currentDegree = -1;
+ } else {
+
+ final double[][] newPols = new double[maxDegree + 1][];
+ if (polynoms != null) {
+ System.arraycopy(polynoms, 0, newPols, 0, polynoms.length);
+ for (int i = polynoms.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];
+ }
+ }
+ polynoms = 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 ((polynoms == null) || (polynoms.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;
+
+ polynoms[0][i] = currentState[i];
+ polynoms[1][i] = ydiff;
+ polynoms[2][i] = aspl;
+ polynoms[3][i] = bspl;
+
+ if (mu < 0) {
+ return;
+ }
+
+ // compute the remaining coefficients
+ final double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
+ polynoms[4][i] = 16 * (yMidDots[0][i] - ph0);
+
+ if (mu > 0) {
+ final double ph1 = ydiff + 0.25 * (aspl - bspl);
+ polynoms[5][i] = 16 * (yMidDots[1][i] - ph1);
+
+ if (mu > 1) {
+ final double ph2 = yp1 - yp0;
+ polynoms[6][i] = 16 * (yMidDots[2][i] - ph2 + polynoms[4][i]);
+
+ if (mu > 2) {
+ final double ph3 = 6 * (bspl - aspl);
+ polynoms[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynoms[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);
+ polynoms[j+4][i] =
+ 16 * (yMidDots[j][i] + fac1 * polynoms[j+2][i] - fac2 * polynoms[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 = polynoms[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 = polynoms[0][i];
+ final double p1 = polynoms[1][i];
+ final double p2 = polynoms[2][i];
+ final double p3 = polynoms[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 = polynoms[currentDegree][i];
+ for (int j = currentDegree - 1; j > 3; --j) {
+ final double d = 1.0 / (j - 3);
+ cDot = d * (theta05 * cDot + c);
+ c = polynoms[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(polynoms[k][l]);
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException {
+
+ // 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) {
+ polynoms[k][l] = in.readDouble();
+ }
+ }
+
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.java
new file mode 100644
index 0000000..6ea8564
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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 (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
+ */
+ 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 (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 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/math/ode/nonstiff/HighamHall54StepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.java
new file mode 100644
index 0000000..f3eb9fb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class HighamHall54StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3583240427587318654L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math.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.
+ */
+ public HighamHall54StepInterpolator() {
+ super();
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ 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));
+ 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);
+
+ 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/math/ode/nonstiff/MidpointIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.java
new file mode 100644
index 0000000..89d1d87
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.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.math.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
+ *
+ * @version $Revision: 810196 $ $Date: 2009-09-01 21:47:46 +0200 (mar. 01 sept. 2009) $
+ * @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/math/ode/nonstiff/MidpointStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.java
new file mode 100644
index 0000000..094ba7a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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 :
+ *
+ * <pre>
+ * y(t_n + theta h) = y (t_n + h) + (1-theta) h [theta y'_1 - (1+theta) y'_2]
+ * </pre>
+ *
+ * where theta belongs to [0 ; 1] and where y'_1 and y'_2 are the two
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see MidpointIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class MidpointStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -865524111506042509L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math.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.
+ */
+ public MidpointStepInterpolator() {
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ final double coeff1 = oneMinusThetaH * theta;
+ final double coeff2 = oneMinusThetaH * (1.0 + theta);
+ final double coeffDot2 = 2 * theta;
+ final double coeffDot1 = 1 - coeffDot2;
+
+ 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/math/ode/nonstiff/RungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java
new file mode 100644
index 0000000..c550f90
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.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.math.ode.nonstiff;
+
+
+import org.apache.commons.math.ode.AbstractIntegrator;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.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
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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} */
+ public double integrate(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0,
+ final double t, final double[] y)
+ throws DerivativeException, IntegratorException {
+
+ sanityChecks(equations, t0, y0, t, y);
+ setEquations(equations);
+ resetEvaluations();
+ final boolean forward = t > t0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, y0.length);
+ }
+ final double[][] yDotK = new double[stages][];
+ for (int i = 0; i < stages; ++i) {
+ yDotK [i] = new double[y0.length];
+ }
+ final double[] yTmp = new double[y0.length];
+ final double[] yDotTmp = new double[y0.length];
+
+ // set up an interpolator sharing the integrator arrays
+ AbstractStepInterpolator interpolator;
+ if (requiresDenseOutput()) {
+ final RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy();
+ rki.reinitialize(this, yTmp, yDotK, forward);
+ interpolator = rki;
+ } else {
+ interpolator = new DummyStepInterpolator(yTmp, yDotK[stages - 1], forward);
+ }
+ interpolator.storeTime(t0);
+
+ // set up integration control objects
+ stepStart = t0;
+ stepSize = forward ? step : -step;
+ for (StepHandler handler : stepHandlers) {
+ handler.reset();
+ }
+ setStateInitialized(false);
+
+ // 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);
+
+ final double stopTime = stepStart;
+ stepStart = Double.NaN;
+ stepSize = Double.NaN;
+ return stopTime;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..97d9fe7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaStepInterpolator.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.math.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math.ode.AbstractIntegrator;
+import org.apache.commons.math.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
+ *
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @since 1.2
+ */
+
+abstract class RungeKuttaStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** 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() {
+ super();
+ 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.
+
+ */
+ public RungeKuttaStepInterpolator(final RungeKuttaStepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.currentState != null) {
+ final int dimension = currentState.length;
+
+ yDotK = new double[interpolator.yDotK.length][];
+ for (int k = 0; k < interpolator.yDotK.length; ++k) {
+ yDotK[k] = new double[dimension];
+ System.arraycopy(interpolator.yDotK[k], 0,
+ yDotK[k], 0, dimension);
+ }
+
+ } else {
+ 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
+ */
+ public void reinitialize(final AbstractIntegrator rkIntegrator,
+ final double[] y, final double[][] yDotArray, final boolean forward) {
+ reinitialize(y, forward);
+ this.yDotK = yDotArray;
+ this.integrator = rkIntegrator;
+ }
+
+ /** {@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;
+ 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 {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+
+ // read the local attributes
+ final int n = (currentState == null) ? -1 : currentState.length;
+ 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/math/ode/nonstiff/ThreeEighthesIntegrator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.java
new file mode 100644
index 0000000..60c39d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.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
+ * @version $Revision: 810196 $ $Date: 2009-09-01 21:47:46 +0200 (mar. 01 sept. 2009) $
+ * @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/math/ode/nonstiff/ThreeEighthesStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.java
new file mode 100644
index 0000000..19d5693
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolator.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.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.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 :
+ *
+ * <pre>
+ * y(t_n + theta h) = y (t_n + h)
+ * - (1 - theta) (h/8) [ (1 - 7 theta + 8 theta^2) y'_1
+ * + 3 (1 + theta - 4 theta^2) y'_2
+ * + 3 (1 + theta) y'_3
+ * + (1 + theta + 4 theta^2) y'_4
+ * ]
+ * </pre>
+ *
+ * where theta belongs to [0 ; 1] and where y'_1 to y'_4 are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ThreeEighthesIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+class ThreeEighthesStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3345024435978721931L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math.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.
+ */
+ public ThreeEighthesStepInterpolator() {
+ }
+
+ /** 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 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)
+ throws DerivativeException {
+
+ final double fourTheta2 = 4 * theta * theta;
+ final double s = oneMinusThetaH / 8.0;
+ 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);
+ 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);
+
+ 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/math/ode/nonstiff/package.html b/src/main/java/org/apache/commons/math/ode/nonstiff/package.html
new file mode 100644
index 0000000..3e68e82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/nonstiff/package.html
@@ -0,0 +1,25 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 613620 $ -->
+<body>
+<p>
+This package provides classes to solve non-stiff Ordinary Differential Equations problems.
+</p>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/ode/package.html b/src/main/java/org/apache/commons/math/ode/package.html
new file mode 100644
index 0000000..d390204
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/package.html
@@ -0,0 +1,167 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 920131 $ -->
+<body>
+<p>
+This package provides classes to solve Ordinary Differential Equations problems.
+</p>
+
+<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>.
+If in addition to <code>y(t)</code> users need to get the
+derivatives 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>, then the
+classes from the <a href="./jacobians/package-summary.html">
+org.apache.commons.math.ode.jacobians</a> package must be used
+instead of the classes in this package.
+</p>
+
+<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.math.ode.sampling.StepInterpolator StepInterpolator}
+abstract class, which are made available to the user at the end of
+each step.
+</p>
+
+<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>
+
+<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.math.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.math.ode.FirstOrderIntegrator
+FirstOrderIntegrator} interface.
+</p>
+
+<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.math.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.math.ode.sampling.StepHandler StepHandler} interface or a
+{@link org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer}
+object wrapping a user-specified object implementing the {@link
+org.apache.commons.math.ode.sampling.FixedStepHandler FixedStepHandler}
+interface into the integrator before calling the {@link
+org.apache.commons.math.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>
+
+<p>
+{@link org.apache.commons.math.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>
+
+<p>
+Other default implementations of the {@link
+org.apache.commons.math.ode.sampling.StepHandler StepHandler} interface are
+available for general needs ({@link
+org.apache.commons.math.ode.sampling.DummyStepHandler DummyStepHandler}, {@link
+org.apache.commons.math.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>
+
+<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.math.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.math.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.math.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>
+
+<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.math.ode.nonstiff.EulerIntegrator Euler}</td><td>1</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.MidpointIntegrator Midpoint}</td><td>2</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.ClassicalRungeKuttaIntegrator Classical Runge-Kutta}</td><td>4</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.GillIntegrator Gill}</td><td>4</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.ThreeEighthesIntegrator 3/8}</td><td>4</td></tr>
+</table>
+</p>
+
+<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.math.ode.nonstiff.HighamHall54Integrator Higham and Hall}</td><td>5</td><td>4</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator Dormand-Prince 5(4)}</td><td>5</td><td>4</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator Dormand-Prince 8(5,3)}</td><td>8</td><td>5 and 3</td></tr>
+<tr><td>{@link org.apache.commons.math.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.math.ode.nonstiff.AdamsBashforthIntegrator Adams-Bashforth}</td><td>variable</td><td>variable</td></tr>
+<tr><td>{@link org.apache.commons.math.ode.nonstiff.AdamsMoultonIntegrator Adams-Moulton}</td><td>variable</td><td>variable</td></tr>
+</table>
+</p>
+
+<p>
+In the table above, the {@link org.apache.commons.math.ode.nonstiff.AdamsBashforthIntegrator
+Adams-Bashforth} and {@link org.apache.commons.math.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.
+</p>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java
new file mode 100644
index 0000000..5cb2979
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/AbstractStepInterpolator.java
@@ -0,0 +1,519 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.sampling;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/** 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.math.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math.ode.SecondOrderIntegrator
+ * @see StepHandler
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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;
+
+ /** 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;
+
+
+ /** 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.math.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;
+ interpolatedState = null;
+ interpolatedDerivatives = null;
+ finalized = false;
+ this.forward = true;
+ this.dirtyState = true;
+ }
+
+ /** Simple constructor.
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param forward integration direction indicator
+ */
+ protected AbstractStepInterpolator(final double[] y, final boolean forward) {
+
+ globalPreviousTime = Double.NaN;
+ globalCurrentTime = Double.NaN;
+ softPreviousTime = Double.NaN;
+ softCurrentTime = Double.NaN;
+ h = Double.NaN;
+ interpolatedTime = Double.NaN;
+
+ currentState = y;
+ interpolatedState = new double[y.length];
+ interpolatedDerivatives = new double[y.length];
+
+ finalized = false;
+ this.forward = forward;
+ this.dirtyState = true;
+
+ }
+
+ /** 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 = interpolator.currentState.clone();
+ interpolatedState = interpolator.interpolatedState.clone();
+ interpolatedDerivatives = interpolator.interpolatedDerivatives.clone();
+ } else {
+ currentState = null;
+ interpolatedState = null;
+ interpolatedDerivatives = null;
+ }
+
+ finalized = interpolator.finalized;
+ forward = interpolator.forward;
+ dirtyState = interpolator.dirtyState;
+
+ }
+
+ /** Reinitialize the instance
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param isForward integration direction indicator
+ */
+ protected void reinitialize(final double[] y, final boolean isForward) {
+
+ globalPreviousTime = Double.NaN;
+ globalCurrentTime = Double.NaN;
+ softPreviousTime = Double.NaN;
+ softCurrentTime = Double.NaN;
+ h = Double.NaN;
+ interpolatedTime = Double.NaN;
+
+ currentState = y;
+ interpolatedState = new double[y.length];
+ interpolatedDerivatives = new double[y.length];
+
+ finalized = false;
+ this.forward = isForward;
+ this.dirtyState = true;
+
+ }
+
+ /** {@inheritDoc} */
+ public StepInterpolator copy() throws DerivativeException {
+
+ // 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
+ * @since 2.2
+ */
+ public double getGlobalPreviousTime() {
+ return globalPreviousTime;
+ }
+
+ /**
+ * Get the current global grid point time.
+ * @return current global grid point time
+ * @since 2.2
+ */
+ 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
+ * @throws DerivativeException this exception is propagated to the caller if the
+ * underlying user function triggers one
+ */
+ protected abstract void computeInterpolatedStateAndDerivatives(double theta,
+ double oneMinusThetaH)
+ throws DerivativeException;
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedState() throws DerivativeException {
+
+ // 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;
+ }
+
+ return interpolatedState;
+
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedDerivatives() throws DerivativeException {
+
+ // 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;
+ }
+
+ return interpolatedDerivatives;
+
+ }
+
+ /**
+ * 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>
+ *
+ * @throws DerivativeException this exception is propagated to the
+ * caller if the underlying user function triggers one
+ */
+ public final void finalizeStep()
+ throws DerivativeException {
+ if (! finalized) {
+ doFinalize();
+ finalized = true;
+ }
+ }
+
+ /**
+ * Really finalize the step.
+ * The default implementation of this method does nothing.
+ * @throws DerivativeException this exception is propagated to the
+ * caller if the underlying user function triggers one
+ */
+ protected void doFinalize()
+ throws DerivativeException {
+ }
+
+ /** {@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);
+
+ 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
+
+ // finalize the step (and don't bother saving the now true flag)
+ try {
+ finalizeStep();
+ } catch (DerivativeException e) {
+ IOException ioe = new IOException(e.getLocalizedMessage());
+ ioe.initCause(e);
+ 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 be set later by the caller
+ * @exception IOException in case of read error
+ */
+ protected double readBaseExternal(final ObjectInput in)
+ throws IOException {
+
+ final int dimension = in.readInt();
+ globalPreviousTime = in.readDouble();
+ globalCurrentTime = in.readDouble();
+ softPreviousTime = in.readDouble();
+ softCurrentTime = in.readDouble();
+ h = in.readDouble();
+ forward = in.readBoolean();
+ 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;
+ interpolatedState = (dimension < 0) ? null : new double[dimension];
+ interpolatedDerivatives = (dimension < 0) ? null : new double[dimension];
+
+ finalized = true;
+
+ return in.readDouble();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java b/src/main/java/org/apache/commons/math/ode/sampling/DummyStepHandler.java
new file mode 100644
index 0000000..cc759b9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/DummyStepHandler.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.math.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
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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;
+ }
+
+ /** Determines whether this handler needs dense output.
+ * Since this handler does nothing, it does not require dense output.
+ * @return always false
+ */
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+
+ /** Reset the step handler.
+ * Initialize the internal data as required before the first step is
+ * handled.
+ */
+ public void reset() {
+ }
+
+ /**
+ * 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/math/ode/sampling/DummyStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.java
new file mode 100644
index 0000000..af28c13
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/DummyStepInterpolator.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.math.ode.sampling;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/** This class is a step interpolator that does nothing.
+ *
+ * <p>This class is used when the {@link StepHandler "step handler"}
+ * set up by the user does not need step interpolation. It does not
+ * recompute the state when {@link AbstractStepInterpolator#setInterpolatedTime
+ * setInterpolatedTime} is called. This implies the interpolated state
+ * is always the state at the end of the current step.</p>
+ *
+ * @see StepHandler
+ *
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ * @since 1.2
+ */
+
+public class DummyStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1708010296707839488L;
+
+ /** Current derivative. */
+ private double[] currentDerivative;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * <code>AbstractStepInterpolator.reinitialize</code> protected 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.math.ode.nonstiff.EmbeddedRungeKuttaIntegrator} uses
+ * the prototyping design pattern to create the step interpolators by
+ * cloning an uninitialized model and latter initializing the copy.
+ */
+ public DummyStepInterpolator() {
+ super();
+ currentDerivative = null;
+ }
+
+ /** Simple constructor.
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param yDot reference to the integrator array holding the state
+ * derivative at some arbitrary point within the step
+ * @param forward integration direction indicator
+ */
+ public DummyStepInterpolator(final double[] y, final double[] yDot, final boolean forward) {
+ super(y, forward);
+ currentDerivative = yDot;
+ }
+
+ /** 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 DummyStepInterpolator(final DummyStepInterpolator interpolator) {
+ super(interpolator);
+ currentDerivative = interpolator.currentDerivative.clone();
+ }
+
+ /** Really copy the finalized instance.
+ * @return a copy of the finalized instance
+ */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new DummyStepInterpolator(this);
+ }
+
+ /** Compute the state at the interpolated time.
+ * In this class, this method does nothing: the interpolated state
+ * is always the state at the end of the current step.
+ * @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
+ */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta, final double oneMinusThetaH) {
+ System.arraycopy(currentState, 0, interpolatedState, 0, currentState.length);
+ System.arraycopy(currentDerivative, 0, interpolatedDerivatives, 0, currentDerivative.length);
+ }
+
+ /** Write the instance to an output channel.
+ * @param out output channel
+ * @exception IOException if the instance cannot be written
+ */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ if (currentDerivative != null) {
+ for (int i = 0; i < currentDerivative.length; ++i) {
+ out.writeDouble(currentDerivative[i]);
+ }
+ }
+
+ }
+
+ /** Read the instance from an input channel.
+ * @param in input channel
+ * @exception IOException if the instance cannot be read
+ */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+
+ if (currentState == null) {
+ currentDerivative = null;
+ } else {
+ currentDerivative = new double[currentState.length];
+ for (int i = 0; i < currentDerivative.length; ++i) {
+ currentDerivative[i] = in.readDouble();
+ }
+ }
+
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java b/src/main/java/org/apache/commons/math/ode/sampling/FixedStepHandler.java
new file mode 100644
index 0000000..190c999
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/FixedStepHandler.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.math.ode.sampling;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/**
+ * 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
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+public interface FixedStepHandler {
+
+ /**
+ * 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
+ * @throws DerivativeException if some error condition is encountered
+ */
+ void handleStep(double t, double[] y, double[] yDot, boolean isLast) throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolator.java b/src/main/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolator.java
new file mode 100644
index 0000000..2090276
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolator.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.sampling;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.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.math.ode.nonstiff.AdamsBashforthIntegrator
+ * @see org.apache.commons.math.ode.nonstiff.AdamsMoultonIntegrator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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
+ */
+ @Override
+ public void reinitialize(final double[] y, final boolean forward) {
+ super.reinitialize(y, forward);
+ 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 Nordiseck 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()
+ * @throws DerivativeException if this call induces an automatic
+ * step finalization that throws one
+ */
+ public double[] getInterpolatedStateVariation()
+ throws DerivativeException {
+ // 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/math/ode/sampling/StepHandler.java b/src/main/java/org/apache/commons/math/ode/sampling/StepHandler.java
new file mode 100644
index 0000000..53c2fbe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/StepHandler.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.math.ode.sampling;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/**
+ * 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.math.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math.ode.SecondOrderIntegrator
+ * @see StepInterpolator
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+public interface StepHandler {
+
+ /** Determines whether this handler needs dense output.
+ * <p>This method allows the integrator to avoid performing extra
+ * computation if the handler does not need dense output. If this
+ * method returns false, the integrator will call the {@link
+ * #handleStep} method with a {@link DummyStepInterpolator} rather
+ * than a custom interpolator.</p>
+ * @return true if the handler needs dense output
+ */
+ boolean requiresDenseOutput();
+
+ /** Reset the step handler.
+ * Initialize the internal data as required before the first step is
+ * handled.
+ */
+ void reset();
+
+ /**
+ * 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.math.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 DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ */
+ void handleStep(StepInterpolator interpolator, boolean isLast) throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/StepInterpolator.java b/src/main/java/org/apache/commons/math/ode/sampling/StepInterpolator.java
new file mode 100644
index 0000000..ceb5a86
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/StepInterpolator.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.math.ode.sampling;
+
+import java.io.Externalizable;
+
+import org.apache.commons.math.ode.DerivativeException;
+
+/** 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.math.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math.ode.SecondOrderIntegrator
+ * @see StepHandler
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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. If a
+ * specific state must be preserved, a copy of the instance must be
+ * created using {@link #copy()}.</p>
+ * @param time time of the interpolated point
+ */
+ 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.</p>
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedDerivatives()
+ * @exception DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ */
+ double[] getInterpolatedState() throws DerivativeException;
+
+ /**
+ * 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.</p>
+ * @return derivatives of the state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedState()
+ * @exception DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ * @since 2.0
+ */
+ double[] getInterpolatedDerivatives() throws DerivativeException;
+
+ /** 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.
+ * @exception DerivativeException if user code called from step interpolator
+ * finalization triggers one
+ * @see #setInterpolatedTime(double)
+ */
+ StepInterpolator copy() throws DerivativeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java b/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java
new file mode 100644
index 0000000..73bcc23
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.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.math.ode.sampling;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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 the beginning time of
+ * the integration t0 and also at times t0+h, t0+2h, ... If the
+ * integration range is an integer multiple of the stepsize, then the
+ * last point handled will be the endpoint of the integration tend, if
+ * not, the last point will belong to the interval [tend - h ;
+ * tend].</p>
+ *
+ * <p>There is no constraint on the integrator, it can use any
+ * timestep it needs (time steps longer or shorter than the fixed time
+ * step and non-integer ratios are all allowed).</p>
+ *
+ * @see StepHandler
+ * @see FixedStepHandler
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ */
+
+public class StepNormalizer implements StepHandler {
+
+ /** Fixed time step. */
+ private double h;
+
+ /** Underlying step handler. */
+ private final FixedStepHandler handler;
+
+ /** Last step time. */
+ private double lastTime;
+
+ /** Last State vector. */
+ private double[] lastState;
+
+ /** Last Derivatives vector. */
+ private double[] lastDerivatives;
+
+ /** Integration direction indicator. */
+ private boolean forward;
+
+ /** Simple constructor.
+ * @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 = FastMath.abs(h);
+ this.handler = handler;
+ reset();
+ }
+
+ /** Determines whether this handler needs dense output.
+ * This handler needs dense output in order to provide data at
+ * regularly spaced steps regardless of the steps the integrator
+ * uses, so this method always returns true.
+ * @return always true
+ */
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+
+ /** Reset the step handler.
+ * Initialize the internal data as required before the first step is
+ * handled.
+ */
+ public void reset() {
+ lastTime = Double.NaN;
+ lastState = null;
+ lastDerivatives = null;
+ forward = true;
+ }
+
+ /**
+ * 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
+ * @throws DerivativeException this exception is propagated to the
+ * caller if the underlying user function triggers one
+ */
+ public void handleStep(final StepInterpolator interpolator, final boolean isLast)
+ throws DerivativeException {
+
+ if (lastState == null) {
+
+ 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;
+ }
+
+ }
+
+ double nextTime = lastTime + h;
+ boolean nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
+ while (nextInStep) {
+
+ // output the stored previous step
+ handler.handleStep(lastTime, lastState, lastDerivatives, false);
+
+ // store the next step
+ lastTime = nextTime;
+ interpolator.setInterpolatedTime(lastTime);
+ System.arraycopy(interpolator.getInterpolatedState(), 0,
+ lastState, 0, lastState.length);
+ System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
+ lastDerivatives, 0, lastDerivatives.length);
+
+ nextTime += h;
+ nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
+
+ }
+
+ if (isLast) {
+ // there will be no more steps,
+ // the stored one should be flagged as being the last
+ handler.handleStep(lastTime, lastState, lastDerivatives, true);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/ode/sampling/package.html b/src/main/java/org/apache/commons/math/ode/sampling/package.html
new file mode 100644
index 0000000..46de4ef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/ode/sampling/package.html
@@ -0,0 +1,60 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 613620 $ -->
+<body>
+<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.math.ode.sampling.StepHandler StepHandler} instance using the
+{@link org.apache.commons.math.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.math.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.math.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.math.ode.sampling.FixedStepHandler FixedStepHandler}
+interface can be used. Objects implementing this interface should be wrapped within a
+{@link org.apache.commons.math.ode.sampling.StepNormalizer StepNormalizer} instance
+in order to be registered to the integrator.
+</p>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateRealOptimizer.java
new file mode 100644
index 0000000..b7b7dbb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateRealOptimizer.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.math.optimization;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+
+/**
+ * This interface represents an optimization algorithm for
+ * {@link DifferentiableMultivariateRealFunction scalar differentiable objective
+ * functions}.
+ * Optimization algorithms find the input point set that either {@link GoalType
+ * maximize or minimize} an objective function.
+ *
+ * @see MultivariateRealOptimizer
+ * @see DifferentiableMultivariateVectorialOptimizer
+ * @version $Revision: 1065484 $ $Date: 2011-01-31 06:45:14 +0100 (lun. 31 janv. 2011) $
+ * @since 2.0
+ */
+public interface DifferentiableMultivariateRealOptimizer {
+
+ /** 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
+ * {@code optimize} method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of iterations
+ */
+ int getIterations();
+
+ /** Set the maximal number of functions evaluations.
+ * @param maxEvaluations maximal number of function 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 objective function.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(DifferentiableMultivariateRealFunction, GoalType, double[]) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the objective function
+ */
+ int getEvaluations();
+
+ /** Get the number of evaluations of the objective function gradient.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(DifferentiableMultivariateRealFunction, GoalType, double[]) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the objective function gradient
+ */
+ int getGradientEvaluations();
+
+ /** Set the convergence checker.
+ * @param checker object to use to check for convergence
+ */
+ void setConvergenceChecker(RealConvergenceChecker checker);
+
+ /** Get the convergence checker.
+ * @return object used to check for convergence
+ */
+ RealConvergenceChecker getConvergenceChecker();
+
+ /** Optimizes an objective function.
+ * @param f objective function
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE}
+ * or {@link GoalType#MINIMIZE}
+ * @param startPoint the start point for optimization
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception FunctionEvaluationException if the objective function throws one during
+ * the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ RealPointValuePair optimize(DifferentiableMultivariateRealFunction f,
+ GoalType goalType,
+ double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateVectorialOptimizer.java b/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateVectorialOptimizer.java
new file mode 100644
index 0000000..51f0763
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/DifferentiableMultivariateVectorialOptimizer.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.math.optimization;
+
+import org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * This interface represents an optimization algorithm for {@link DifferentiableMultivariateVectorialFunction
+ * vectorial differentiable objective functions}.
+ * <p>Optimization algorithms find the input point set that either {@link GoalType
+ * maximize or minimize} an objective function.</p>
+ * @see MultivariateRealOptimizer
+ * @see DifferentiableMultivariateRealOptimizer
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public interface DifferentiableMultivariateVectorialOptimizer {
+
+ /** 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.
+ * @return number of iterations
+ */
+ int getIterations();
+
+ /** Set the maximal number of functions evaluations.
+ * @param maxEvaluations maximal number of function 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 objective function.
+ * <p>
+ * The number of evaluation correspond to the last call to the
+ * {@link #optimize(DifferentiableMultivariateVectorialFunction,
+ * double[], double[], double[]) optimize} method. It is 0 if
+ * the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the objective function
+ */
+ int getEvaluations();
+
+ /** Get the number of evaluations of the objective function jacobian .
+ * <p>
+ * The number of evaluation correspond to the last call to the
+ * {@link #optimize(DifferentiableMultivariateVectorialFunction,
+ * double[], double[], double[]) optimize} method. It is 0 if
+ * the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the objective function jacobian
+ */
+ int getJacobianEvaluations();
+
+ /** Set the convergence checker.
+ * @param checker object to use to check for convergence
+ */
+ void setConvergenceChecker(VectorialConvergenceChecker checker);
+
+ /** Get the convergence checker.
+ * @return object used to check for convergence
+ */
+ VectorialConvergenceChecker getConvergenceChecker();
+
+ /** Optimizes an objective function.
+ * <p>
+ * Optimization is considered to be a weighted least-squares minimization.
+ * The cost function to be minimized is
+ * &sum;weight<sub>i</sub>(objective<sub>i</sub>-target<sub>i</sub>)<sup>2</sup>
+ * </p>
+ * @param f objective function
+ * @param target target value for the objective functions at optimum
+ * @param weights weight for the least squares cost computation
+ * @param startPoint the start point for optimization
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception FunctionEvaluationException if the objective function throws one during
+ * the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ VectorialPointValuePair optimize(DifferentiableMultivariateVectorialFunction f,
+ double[] target, double[] weights,
+ double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/GoalType.java b/src/main/java/org/apache/commons/math/optimization/GoalType.java
new file mode 100644
index 0000000..1392d27
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/GoalType.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.math.optimization;
+
+import java.io.Serializable;
+
+/**
+ * Goal type for an optimization problem.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+public enum GoalType implements Serializable {
+
+ /** Maximization goal. */
+ MAXIMIZE,
+
+ /** Minimization goal. */
+ MINIMIZE
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/LeastSquaresConverter.java b/src/main/java/org/apache/commons/math/optimization/LeastSquaresConverter.java
new file mode 100644
index 0000000..0ed2efd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/LeastSquaresConverter.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateVectorialFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.RealMatrix;
+
+/** This class converts {@link MultivariateVectorialFunction vectorial
+ * objective functions} to {@link MultivariateRealFunction 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>
+ * <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 MultivariateRealFunction} 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>
+ * <p>
+ * This class support combination of residuals with or without weights and correlations.
+ * </p>
+ *
+ * @see MultivariateRealFunction
+ * @see MultivariateVectorialFunction
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+
+public class LeastSquaresConverter implements MultivariateRealFunction {
+
+ /** Underlying vectorial function. */
+ private final MultivariateVectorialFunction 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 MultivariateVectorialFunction 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>
+ * <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 FunctionEvaluationException} 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
+ * @exception IllegalArgumentException if the observations vector and the weights
+ * vector dimensions don't match (objective function dimension is checked only when
+ * the {@link #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(final MultivariateVectorialFunction function,
+ final double[] observations, final double[] weights)
+ throws IllegalArgumentException {
+ if (observations.length != weights.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ 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>
+ * <p>
+ * The array computed by the objective function, the observations array and the
+ * the scaling matrix must have consistent sizes or a {@link FunctionEvaluationException}
+ * 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
+ * @exception IllegalArgumentException if the observations vector and the scale
+ * matrix dimensions don't match (objective function dimension is checked only when
+ * the {@link #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(final MultivariateVectorialFunction function,
+ final double[] observations, final RealMatrix scale)
+ throws IllegalArgumentException {
+ if (observations.length != scale.getColumnDimension()) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ 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) throws FunctionEvaluationException {
+
+ // compute residuals
+ final double[] residuals = function.value(point);
+ if (residuals.length != observations.length) {
+ throw new FunctionEvaluationException(point,LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ 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/math/optimization/MultiStartDifferentiableMultivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizer.java
new file mode 100644
index 0000000..9cde37b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizer.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.math.optimization;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link DifferentiableMultivariateRealOptimizer} 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.
+ * </p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public class MultiStartDifferentiableMultivariateRealOptimizer
+ implements DifferentiableMultivariateRealOptimizer {
+
+ /** Underlying classical optimizer. */
+ private final DifferentiableMultivariateRealOptimizer optimizer;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed for all starts. */
+ private int totalIterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+
+ /** Number of gradient evaluations already performed for all starts. */
+ private int totalGradientEvaluations;
+
+ /** Number of starts to go. */
+ private int starts;
+
+ /** Random generator for multi-start. */
+ private RandomVectorGenerator generator;
+
+ /** Found optima. */
+ private RealPointValuePair[] 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 (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 MultiStartDifferentiableMultivariateRealOptimizer(final DifferentiableMultivariateRealOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ this.optimizer = optimizer;
+ this.totalIterations = 0;
+ this.totalEvaluations = 0;
+ this.totalGradientEvaluations = 0;
+ this.starts = starts;
+ this.generator = generator;
+ this.optima = null;
+ setMaxIterations(Integer.MAX_VALUE);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** Get all the optima found during the last call to {@link
+ * #optimize(DifferentiableMultivariateRealFunction, GoalType, double[])
+ * optimize}.
+ * <p>The optimizer stores all the optima found during a set of
+ * restarts. The {@link #optimize(DifferentiableMultivariateRealFunction,
+ * 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(DifferentiableMultivariateRealFunction, GoalType, double[])
+ * optimize} method.
+ * </p>
+ * <p>
+ * 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(DifferentiableMultivariateRealFunction,
+ * GoalType, double[]) optimize} method did throw a {@link
+ * org.apache.commons.math.ConvergenceException ConvergenceException}).
+ * This also means that if the first element is non null, it is the best
+ * point found across all starts.</p>
+ * @return array containing the optima
+ * @exception IllegalStateException if {@link #optimize(DifferentiableMultivariateRealFunction,
+ * GoalType, double[]) optimize} has not been called
+ */
+ public RealPointValuePair[] getOptima() throws IllegalStateException {
+ if (optima == null) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return totalIterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getGradientEvaluations() {
+ return totalGradientEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(RealConvergenceChecker checker) {
+ optimizer.setConvergenceChecker(checker);
+ }
+
+ /** {@inheritDoc} */
+ public RealConvergenceChecker getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public RealPointValuePair optimize(final DifferentiableMultivariateRealFunction f,
+ final GoalType goalType,
+ double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, FunctionEvaluationException {
+
+ optima = new RealPointValuePair[starts];
+ totalIterations = 0;
+ totalEvaluations = 0;
+ totalGradientEvaluations = 0;
+
+ // multi-start loop
+ for (int i = 0; i < starts; ++i) {
+
+ try {
+ optimizer.setMaxIterations(maxIterations - totalIterations);
+ optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
+ optima[i] = optimizer.optimize(f, goalType,
+ (i == 0) ? startPoint : generator.nextVector());
+ } catch (FunctionEvaluationException fee) {
+ optima[i] = null;
+ } catch (OptimizationException oe) {
+ optima[i] = null;
+ }
+
+ totalIterations += optimizer.getIterations();
+ totalEvaluations += optimizer.getEvaluations();
+ totalGradientEvaluations += optimizer.getGradientEvaluations();
+
+ }
+
+ // sort the optima from best to worst, followed by null elements
+ Arrays.sort(optima, new Comparator<RealPointValuePair>() {
+ public int compare(final RealPointValuePair o1, final RealPointValuePair 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 (goalType == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ });
+
+ if (optima[0] == null) {
+ throw new OptimizationException(
+ LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
+ starts);
+ }
+
+ // return the found point given the best objective function value
+ return optima[0];
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizer.java b/src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizer.java
new file mode 100644
index 0000000..d2d5268
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizer.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.math.optimization;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link DifferentiableMultivariateVectorialOptimizer} 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.
+ * </p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public class MultiStartDifferentiableMultivariateVectorialOptimizer
+ implements DifferentiableMultivariateVectorialOptimizer {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 9206382258980561530L;
+
+ /** Underlying classical optimizer. */
+ private final DifferentiableMultivariateVectorialOptimizer optimizer;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed for all starts. */
+ private int totalIterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+
+ /** Number of jacobian evaluations already performed for all starts. */
+ private int totalJacobianEvaluations;
+
+ /** Number of starts to go. */
+ private int starts;
+
+ /** Random generator for multi-start. */
+ private RandomVectorGenerator generator;
+
+ /** Found optima. */
+ private VectorialPointValuePair[] 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 (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 MultiStartDifferentiableMultivariateVectorialOptimizer(
+ final DifferentiableMultivariateVectorialOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ this.optimizer = optimizer;
+ this.totalIterations = 0;
+ this.totalEvaluations = 0;
+ this.totalJacobianEvaluations = 0;
+ this.starts = starts;
+ this.generator = generator;
+ this.optima = null;
+ setMaxIterations(Integer.MAX_VALUE);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** Get all the optima found during the last call to {@link
+ * #optimize(DifferentiableMultivariateVectorialFunction,
+ * double[], double[], double[]) optimize}.
+ * <p>The optimizer stores all the optima found during a set of
+ * restarts. The {@link #optimize(DifferentiableMultivariateVectorialFunction,
+ * 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(DifferentiableMultivariateVectorialFunction, double[],
+ * double[], double[]) optimize} method.
+ * </p>
+ * <p>
+ * 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(DifferentiableMultivariateVectorialFunction,
+ * double[], double[], double[]) optimize} method did throw a {@link
+ * org.apache.commons.math.ConvergenceException ConvergenceException}).
+ * This also means that if the first element is non null, it is the best
+ * point found across all starts.</p>
+ * @return array containing the optima
+ * @exception IllegalStateException if {@link #optimize(DifferentiableMultivariateVectorialFunction,
+ * double[], double[], double[]) optimize} has not been called
+ */
+ public VectorialPointValuePair[] getOptima() throws IllegalStateException {
+ if (optima == null) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return totalIterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getJacobianEvaluations() {
+ return totalJacobianEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(VectorialConvergenceChecker checker) {
+ optimizer.setConvergenceChecker(checker);
+ }
+
+ /** {@inheritDoc} */
+ public VectorialConvergenceChecker getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public VectorialPointValuePair optimize(final DifferentiableMultivariateVectorialFunction f,
+ final double[] target, final double[] weights,
+ final double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ optima = new VectorialPointValuePair[starts];
+ totalIterations = 0;
+ totalEvaluations = 0;
+ totalJacobianEvaluations = 0;
+
+ // multi-start loop
+ for (int i = 0; i < starts; ++i) {
+
+ try {
+ optimizer.setMaxIterations(maxIterations - totalIterations);
+ optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
+ optima[i] = optimizer.optimize(f, target, weights,
+ (i == 0) ? startPoint : generator.nextVector());
+ } catch (FunctionEvaluationException fee) {
+ optima[i] = null;
+ } catch (OptimizationException oe) {
+ optima[i] = null;
+ }
+
+ totalIterations += optimizer.getIterations();
+ totalEvaluations += optimizer.getEvaluations();
+ totalJacobianEvaluations += optimizer.getJacobianEvaluations();
+
+ }
+
+ // sort the optima from best to worst, followed by null elements
+ Arrays.sort(optima, new Comparator<VectorialPointValuePair>() {
+ public int compare(final VectorialPointValuePair o1, final VectorialPointValuePair 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 VectorialPointValuePair 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;
+ }
+ });
+
+ if (optima[0] == null) {
+ throw new OptimizationException(
+ LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
+ starts);
+ }
+
+ // return the found point given the best objective function value
+ return optima[0];
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizer.java
new file mode 100644
index 0000000..fdba1d5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizer.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.math.optimization;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link MultivariateRealOptimizer} 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.
+ * </p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public class MultiStartMultivariateRealOptimizer
+ implements MultivariateRealOptimizer {
+
+ /** Underlying classical optimizer. */
+ private final MultivariateRealOptimizer optimizer;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of iterations already performed for all starts. */
+ private int totalIterations;
+
+ /** 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 RealPointValuePair[] 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 (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 MultiStartMultivariateRealOptimizer(final MultivariateRealOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ this.optimizer = optimizer;
+ this.totalIterations = 0;
+ this.totalEvaluations = 0;
+ this.starts = starts;
+ this.generator = generator;
+ this.optima = null;
+ setMaxIterations(Integer.MAX_VALUE);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** Get all the optima found during the last call to {@link
+ * #optimize(MultivariateRealFunction, GoalType, double[]) optimize}.
+ * <p>The optimizer stores all the optima found during a set of
+ * restarts. The {@link #optimize(MultivariateRealFunction, 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(MultivariateRealFunction, GoalType, double[]) optimize}
+ * method.
+ * </p>
+ * <p>
+ * 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(MultivariateRealFunction,
+ * GoalType, double[]) optimize} method did throw a {@link
+ * org.apache.commons.math.ConvergenceException ConvergenceException}).
+ * This also means that if the first element is non null, it is the best
+ * point found across all starts.</p>
+ * @return array containing the optima
+ * @exception IllegalStateException if {@link #optimize(MultivariateRealFunction,
+ * GoalType, double[]) optimize} has not been called
+ */
+ public RealPointValuePair[] getOptima() throws IllegalStateException {
+ if (optima == null) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return totalIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(RealConvergenceChecker checker) {
+ optimizer.setConvergenceChecker(checker);
+ }
+
+ /** {@inheritDoc} */
+ public RealConvergenceChecker getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public RealPointValuePair optimize(final MultivariateRealFunction f,
+ final GoalType goalType,
+ double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, FunctionEvaluationException {
+
+ optima = new RealPointValuePair[starts];
+ totalIterations = 0;
+ totalEvaluations = 0;
+
+ // multi-start loop
+ for (int i = 0; i < starts; ++i) {
+
+ try {
+ optimizer.setMaxIterations(maxIterations - totalIterations);
+ optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
+ optima[i] = optimizer.optimize(f, goalType,
+ (i == 0) ? startPoint : generator.nextVector());
+ } catch (FunctionEvaluationException fee) {
+ optima[i] = null;
+ } catch (OptimizationException oe) {
+ optima[i] = null;
+ }
+
+ totalIterations += optimizer.getIterations();
+ totalEvaluations += optimizer.getEvaluations();
+
+ }
+
+ // sort the optima from best to worst, followed by null elements
+ Arrays.sort(optima, new Comparator<RealPointValuePair>() {
+ public int compare(final RealPointValuePair o1, final RealPointValuePair 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 (goalType == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ });
+
+ if (optima[0] == null) {
+ throw new OptimizationException(
+ LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
+ starts);
+ }
+
+ // return the found point given the best objective function value
+ return optima[0];
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizer.java
new file mode 100644
index 0000000..b5ccc5f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizer.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.math.optimization;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.random.RandomGenerator;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Special implementation of the {@link UnivariateRealOptimizer} 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.
+ * </p>
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public class MultiStartUnivariateRealOptimizer implements UnivariateRealOptimizer {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 5983375963110961019L;
+
+ /** Underlying classical optimizer. */
+ private final UnivariateRealOptimizer optimizer;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of iterations already performed for all starts. */
+ private int totalIterations;
+
+ /** 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 double[] optima;
+
+ /** Found function values at optima. */
+ private double[] optimaValues;
+
+ /**
+ * 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 generator to use for restarts
+ */
+ public MultiStartUnivariateRealOptimizer(final UnivariateRealOptimizer optimizer,
+ final int starts,
+ final RandomGenerator generator) {
+ this.optimizer = optimizer;
+ this.totalIterations = 0;
+ this.starts = starts;
+ this.generator = generator;
+ this.optima = null;
+ setMaximalIterationCount(Integer.MAX_VALUE);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** {@inheritDoc} */
+ public double getFunctionValue() {
+ return optimaValues[0];
+ }
+
+ /** {@inheritDoc} */
+ public double getResult() {
+ return optima[0];
+ }
+
+ /** {@inheritDoc} */
+ public double getAbsoluteAccuracy() {
+ return optimizer.getAbsoluteAccuracy();
+ }
+
+ /** {@inheritDoc} */
+ public int getIterationCount() {
+ return totalIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaximalIterationCount() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public double getRelativeAccuracy() {
+ return optimizer.getRelativeAccuracy();
+ }
+
+ /** {@inheritDoc} */
+ public void resetAbsoluteAccuracy() {
+ optimizer.resetAbsoluteAccuracy();
+ }
+
+ /** {@inheritDoc} */
+ public void resetMaximalIterationCount() {
+ optimizer.resetMaximalIterationCount();
+ }
+
+ /** {@inheritDoc} */
+ public void resetRelativeAccuracy() {
+ optimizer.resetRelativeAccuracy();
+ }
+
+ /** {@inheritDoc} */
+ public void setAbsoluteAccuracy(double accuracy) {
+ optimizer.setAbsoluteAccuracy(accuracy);
+ }
+
+ /** {@inheritDoc} */
+ public void setMaximalIterationCount(int count) {
+ this.maxIterations = count;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setRelativeAccuracy(double accuracy) {
+ optimizer.setRelativeAccuracy(accuracy);
+ }
+
+ /** Get all the optima found during the last call to {@link
+ * #optimize(UnivariateRealFunction, GoalType, double, double) optimize}.
+ * <p>The optimizer stores all the optima found during a set of
+ * restarts. The {@link #optimize(UnivariateRealFunction, 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(UnivariateRealFunction, GoalType, double, double) optimize}
+ * method.
+ * </p>
+ * <p>
+ * 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 Double.NaN elements
+ * corresponding to the runs that did not converge. This means all
+ * elements will be NaN if the {@link #optimize(UnivariateRealFunction,
+ * GoalType, double, double) optimize} method did throw a {@link
+ * ConvergenceException ConvergenceException}). This also means that
+ * if the first element is not NaN, it is the best point found across
+ * all starts.</p>
+ * @return array containing the optima
+ * @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
+ * GoalType, double, double) optimize} has not been called
+ * @see #getOptimaValues()
+ */
+ public double[] getOptima() throws IllegalStateException {
+ if (optima == null) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** Get all the function values at optima found during the last call to {@link
+ * #optimize(UnivariateRealFunction, GoalType, double, double) optimize}.
+ * <p>
+ * 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 Double.NaN elements
+ * corresponding to the runs that did not converge. This means all
+ * elements will be NaN if the {@link #optimize(UnivariateRealFunction,
+ * GoalType, double, double) optimize} method did throw a {@link
+ * ConvergenceException ConvergenceException}). This also means that
+ * if the first element is not NaN, it is the best point found across
+ * all starts.</p>
+ * @return array containing the optima
+ * @exception IllegalStateException if {@link #optimize(UnivariateRealFunction,
+ * GoalType, double, double) optimize} has not been called
+ * @see #getOptima()
+ */
+ public double[] getOptimaValues() throws IllegalStateException {
+ if (optimaValues == null) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optimaValues.clone();
+ }
+
+ /** {@inheritDoc} */
+ public double optimize(final UnivariateRealFunction f, final GoalType goalType,
+ final double min, final double max)
+ throws ConvergenceException, FunctionEvaluationException {
+
+ optima = new double[starts];
+ optimaValues = new double[starts];
+ totalIterations = 0;
+ totalEvaluations = 0;
+
+ // multi-start loop
+ for (int i = 0; i < starts; ++i) {
+
+ try {
+ optimizer.setMaximalIterationCount(maxIterations - totalIterations);
+ optimizer.setMaxEvaluations(maxEvaluations - totalEvaluations);
+ final double bound1 = (i == 0) ? min : min + generator.nextDouble() * (max - min);
+ final double bound2 = (i == 0) ? max : min + generator.nextDouble() * (max - min);
+ optima[i] = optimizer.optimize(f, goalType,
+ FastMath.min(bound1, bound2),
+ FastMath.max(bound1, bound2));
+ optimaValues[i] = optimizer.getFunctionValue();
+ } catch (FunctionEvaluationException fee) {
+ optima[i] = Double.NaN;
+ optimaValues[i] = Double.NaN;
+ } catch (ConvergenceException ce) {
+ optima[i] = Double.NaN;
+ optimaValues[i] = Double.NaN;
+ }
+
+ totalIterations += optimizer.getIterationCount();
+ totalEvaluations += optimizer.getEvaluations();
+
+ }
+
+ // sort the optima from best to worst, followed by NaN elements
+ int lastNaN = optima.length;
+ for (int i = 0; i < lastNaN; ++i) {
+ if (Double.isNaN(optima[i])) {
+ optima[i] = optima[--lastNaN];
+ optima[lastNaN + 1] = Double.NaN;
+ optimaValues[i] = optimaValues[--lastNaN];
+ optimaValues[lastNaN + 1] = Double.NaN;
+ }
+ }
+
+ double currX = optima[0];
+ double currY = optimaValues[0];
+ for (int j = 1; j < lastNaN; ++j) {
+ final double prevY = currY;
+ currX = optima[j];
+ currY = optimaValues[j];
+ if ((goalType == GoalType.MAXIMIZE) ^ (currY < prevY)) {
+ // the current element should be inserted closer to the beginning
+ int i = j - 1;
+ double mIX = optima[i];
+ double mIY = optimaValues[i];
+ while ((i >= 0) && ((goalType == GoalType.MAXIMIZE) ^ (currY < mIY))) {
+ optima[i + 1] = mIX;
+ optimaValues[i + 1] = mIY;
+ if (i-- != 0) {
+ mIX = optima[i];
+ mIY = optimaValues[i];
+ } else {
+ mIX = Double.NaN;
+ mIY = Double.NaN;
+ }
+ }
+ optima[i + 1] = currX;
+ optimaValues[i + 1] = currY;
+ currX = optima[j];
+ currY = optimaValues[j];
+ }
+ }
+
+ if (Double.isNaN(optima[0])) {
+ throw new OptimizationException(
+ LocalizedFormats.NO_CONVERGENCE_WITH_ANY_START_POINT,
+ starts);
+ }
+
+ // return the found point given the best objective function value
+ return optima[0];
+
+ }
+
+ /** {@inheritDoc} */
+ public double optimize(final UnivariateRealFunction f, final GoalType goalType,
+ final double min, final double max, final double startValue)
+ throws ConvergenceException, FunctionEvaluationException {
+ return optimize(f, goalType, min, max);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/MultivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/MultivariateRealOptimizer.java
new file mode 100644
index 0000000..d63afcd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/MultivariateRealOptimizer.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.math.optimization;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link MultivariateRealFunction
+ * scalar objective functions}.
+ * <p>Optimization algorithms find the input point set that either {@link GoalType
+ * maximize or minimize} an objective function.</p>
+ * @see DifferentiableMultivariateRealOptimizer
+ * @see DifferentiableMultivariateVectorialOptimizer
+ * @version $Revision: 1065481 $ $Date: 2011-01-31 06:31:41 +0100 (lun. 31 janv. 2011) $
+ * @since 2.0
+ */
+public interface MultivariateRealOptimizer {
+
+ /** Set the maximal number of iterations of the algorithm.
+ * @param maxIterations maximal number of algorithm iterations
+ */
+ void setMaxIterations(int maxIterations);
+
+ /** Get the maximal number of iterations of the algorithm.
+ * @return maximal number of iterations
+ */
+ int getMaxIterations();
+
+ /** Set the maximal number of functions evaluations.
+ * @param maxEvaluations maximal number of function evaluations
+ */
+ void setMaxEvaluations(int maxEvaluations);
+
+ /** Get the maximal number of functions evaluations.
+ * @return maximal number of functions evaluations
+ */
+ int getMaxEvaluations();
+
+ /** Get the number of iterations realized by the algorithm.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(MultivariateRealFunction, GoalType, double[]) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of iterations
+ */
+ int getIterations();
+
+ /** Get the number of evaluations of the objective function.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(MultivariateRealFunction, GoalType, double[]) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of evaluations of the objective function
+ */
+ int getEvaluations();
+
+ /** Set the convergence checker.
+ * @param checker object to use to check for convergence
+ */
+ void setConvergenceChecker(RealConvergenceChecker checker);
+
+ /** Get the convergence checker.
+ * @return object used to check for convergence
+ */
+ RealConvergenceChecker getConvergenceChecker();
+
+ /** Optimizes an objective function.
+ * @param f objective function
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE}
+ * or {@link GoalType#MINIMIZE}
+ * @param startPoint the start point for optimization
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception FunctionEvaluationException if the objective function throws one during
+ * the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ RealPointValuePair optimize(MultivariateRealFunction f,
+ GoalType goalType,
+ double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/OptimizationException.java b/src/main/java/org/apache/commons/math/optimization/OptimizationException.java
new file mode 100644
index 0000000..b76f51a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/OptimizationException.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.math.optimization;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+
+/**
+ * This class represents exceptions thrown by optimizers.
+ *
+ * @version $Revision: 1044015 $ $Date: 2010-12-09 17:06:26 +0100 (jeu. 09 déc. 2010) $
+ * @since 1.2
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+
+public class OptimizationException extends ConvergenceException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4605887730798282127L;
+
+ /**
+ * 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)
+ * @deprecated as of 2.2 replaced by {@link #OptimizationException(Localizable, Object...)}
+ */
+ @Deprecated
+ public OptimizationException(String specifier, Object ... parts) {
+ this(new DummyLocalizable(specifier), parts);
+ }
+
+ /**
+ * 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 OptimizationException(Localizable specifier, Object ... parts) {
+ super(specifier, parts);
+ }
+
+ /**
+ * Create an exception with a given root cause.
+ * @param cause the exception or error that caused this exception to be thrown
+ */
+ public OptimizationException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/RealConvergenceChecker.java b/src/main/java/org/apache/commons/math/optimization/RealConvergenceChecker.java
new file mode 100644
index 0000000..0ab37f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/RealConvergenceChecker.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.math.optimization;
+
+/** This interface specifies how to check if an {@link MultivariateRealOptimizer optimization
+ * algorithm} has converged.
+ *
+ * <p>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.</p>
+ * <p>For convenience, two implementations that fit simple needs are already provided:
+ * {@link SimpleScalarValueChecker} and {@link SimpleRealPointChecker}. The first
+ * one considers convergence is reached when the objective function value does not
+ * change much anymore, it does not use the point set at all. The second one
+ * considers convergence is reached when the input point set does not change
+ * much anymore, it does not use objective function value at all.</p>
+ *
+ * @version $Revision: 799857 $ $Date: 2009-08-01 15:07:12 +0200 (sam. 01 août 2009) $
+ * @since 2.0
+ */
+
+public interface RealConvergenceChecker {
+
+ /** Check if the optimization algorithm has converged considering the last points.
+ * <p>
+ * 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.
+ * </p>
+ * @param iteration index of current iteration
+ * @param previous point from previous iteration
+ * @param current point from current iteration
+ * @return true if the algorithm is considered to have converged
+ */
+ boolean converged(int iteration, RealPointValuePair previous, RealPointValuePair current);
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/RealPointValuePair.java b/src/main/java/org/apache/commons/math/optimization/RealPointValuePair.java
new file mode 100644
index 0000000..25ecec9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/RealPointValuePair.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.math.optimization;
+
+import java.io.Serializable;
+
+
+/**
+ * This class holds a point and the value of an objective function at this point.
+ * <p>This is a simple immutable container.</p>
+ * @see VectorialPointValuePair
+ * @see org.apache.commons.math.analysis.MultivariateRealFunction
+ * @version $Revision: 980981 $ $Date: 2010-07-31 00:03:04 +0200 (sam. 31 juil. 2010) $
+ * @since 2.0
+ */
+public class RealPointValuePair implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1003888396256744753L;
+
+ /** Point coordinates. */
+ 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 coordinates (the built instance will store
+ * a copy of the array, not the array passed as argument)
+ * @param value value of an objective function at the point
+ */
+ public RealPointValuePair(final double[] point, final double value) {
+ this.point = (point == null) ? null : point.clone();
+ this.value = value;
+ }
+
+ /** Build a point/objective function value pair.
+ * @param point point coordinates (the built instance will store
+ * a copy of the array, not the array passed as argument)
+ * @param value value of an objective function at the point
+ * @param copyArray if true, the input array will be copied, otherwise
+ * it will be referenced
+ */
+ public RealPointValuePair(final double[] point, final double value,
+ final boolean copyArray) {
+ this.point = copyArray ?
+ ((point == null) ? null : point.clone()) :
+ point;
+ this.value = value;
+ }
+
+ /** Get the point.
+ * @return a copy of the stored point
+ */
+ public double[] getPoint() {
+ return (point == null) ? null : point.clone();
+ }
+
+ /** Get a reference to the point.
+ * <p>This method is provided as a convenience to avoid copying
+ * the array, the elements of the array should <em>not</em> be modified.</p>
+ * @return a reference to the internal array storing the point
+ */
+ public double[] getPointRef() {
+ 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/math/optimization/SimpleRealPointChecker.java b/src/main/java/org/apache/commons/math/optimization/SimpleRealPointChecker.java
new file mode 100644
index 0000000..6cf0725
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/SimpleRealPointChecker.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.math.optimization;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Simple implementation of the {@link RealConvergenceChecker} 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.
+ * </p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class SimpleRealPointChecker implements RealConvergenceChecker {
+
+ /** Default relative threshold. */
+ private static final double DEFAULT_RELATIVE_THRESHOLD = 100 * MathUtils.EPSILON;
+
+ /** Default absolute threshold. */
+ private static final double DEFAULT_ABSOLUTE_THRESHOLD = 100 * MathUtils.SAFE_MIN;
+
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /** Build an instance with default threshold.
+ */
+ public SimpleRealPointChecker() {
+ this.relativeThreshold = DEFAULT_RELATIVE_THRESHOLD;
+ this.absoluteThreshold = DEFAULT_ABSOLUTE_THRESHOLD;
+ }
+
+ /** Build an instance with a specified threshold.
+ * <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.
+ * </p>
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleRealPointChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final RealPointValuePair previous,
+ final RealPointValuePair current) {
+ final double[] p = previous.getPoint();
+ final double[] c = current.getPoint();
+ for (int i = 0; i < p.length; ++i) {
+ final double difference = FastMath.abs(p[i] - c[i]);
+ final double size = FastMath.max(FastMath.abs(p[i]), FastMath.abs(c[i]));
+ if ((difference > (size * relativeThreshold)) && (difference > absoluteThreshold)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/SimpleScalarValueChecker.java b/src/main/java/org/apache/commons/math/optimization/SimpleScalarValueChecker.java
new file mode 100644
index 0000000..fbfd30c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/SimpleScalarValueChecker.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.math.optimization;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Simple implementation of the {@link RealConvergenceChecker} 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.
+ * </p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class SimpleScalarValueChecker implements RealConvergenceChecker {
+
+ /** Default relative threshold. */
+ private static final double DEFAULT_RELATIVE_THRESHOLD = 100 * MathUtils.EPSILON;
+
+ /** Default absolute threshold. */
+ private static final double DEFAULT_ABSOLUTE_THRESHOLD = 100 * MathUtils.SAFE_MIN;
+
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /** Build an instance with default threshold.
+ */
+ public SimpleScalarValueChecker() {
+ this.relativeThreshold = DEFAULT_RELATIVE_THRESHOLD;
+ this.absoluteThreshold = DEFAULT_ABSOLUTE_THRESHOLD;
+ }
+
+ /** Build an instance with a specified threshold.
+ * <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.
+ * </p>
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleScalarValueChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final RealPointValuePair previous,
+ final RealPointValuePair current) {
+ 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 * relativeThreshold)) || (difference <= absoluteThreshold);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/SimpleVectorialPointChecker.java b/src/main/java/org/apache/commons/math/optimization/SimpleVectorialPointChecker.java
new file mode 100644
index 0000000..0c69ca8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/SimpleVectorialPointChecker.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.math.optimization;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Simple implementation of the {@link VectorialConvergenceChecker} 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.
+ * </p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class SimpleVectorialPointChecker implements VectorialConvergenceChecker {
+
+ /** Default relative threshold. */
+ private static final double DEFAULT_RELATIVE_THRESHOLD = 100 * MathUtils.EPSILON;
+
+ /** Default absolute threshold. */
+ private static final double DEFAULT_ABSOLUTE_THRESHOLD = 100 * MathUtils.SAFE_MIN;
+
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /** Build an instance with default threshold.
+ */
+ public SimpleVectorialPointChecker() {
+ this.relativeThreshold = DEFAULT_RELATIVE_THRESHOLD;
+ this.absoluteThreshold = DEFAULT_ABSOLUTE_THRESHOLD;
+ }
+
+ /** Build an instance with a specified threshold.
+ * <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.
+ * </p>
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleVectorialPointChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final VectorialPointValuePair previous,
+ final VectorialPointValuePair current) {
+ final double[] p = previous.getPointRef();
+ final double[] c = current.getPointRef();
+ 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 * relativeThreshold)) &&
+ (difference > absoluteThreshold)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/SimpleVectorialValueChecker.java b/src/main/java/org/apache/commons/math/optimization/SimpleVectorialValueChecker.java
new file mode 100644
index 0000000..8be67ee
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/SimpleVectorialValueChecker.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.math.optimization;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Simple implementation of the {@link VectorialConvergenceChecker} 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.
+ * </p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class SimpleVectorialValueChecker implements VectorialConvergenceChecker {
+
+ /** Default relative threshold. */
+ private static final double DEFAULT_RELATIVE_THRESHOLD = 100 * MathUtils.EPSILON;
+
+ /** Default absolute threshold. */
+ private static final double DEFAULT_ABSOLUTE_THRESHOLD = 100 * MathUtils.SAFE_MIN;
+
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /** Build an instance with default threshold.
+ */
+ public SimpleVectorialValueChecker() {
+ this.relativeThreshold = DEFAULT_RELATIVE_THRESHOLD;
+ this.absoluteThreshold = DEFAULT_ABSOLUTE_THRESHOLD;
+ }
+
+ /** Build an instance with a specified threshold.
+ * <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.
+ * </p>
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleVectorialValueChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final VectorialPointValuePair previous,
+ final VectorialPointValuePair current) {
+ 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 * relativeThreshold)) &&
+ (difference > absoluteThreshold)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/UnivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/UnivariateRealOptimizer.java
new file mode 100644
index 0000000..df17133
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/UnivariateRealOptimizer.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.math.optimization;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.ConvergingAlgorithm;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+
+/**
+ * Interface for (univariate real) optimization algorithms.
+ *
+ * @version $Revision: 1073658 $ $Date: 2011-02-23 10:45:42 +0100 (mer. 23 févr. 2011) $
+ * @since 2.0
+ */
+public interface UnivariateRealOptimizer extends ConvergingAlgorithm {
+
+ /** Set the maximal number of functions evaluations.
+ * @param maxEvaluations maximal number of function evaluations
+ */
+ void setMaxEvaluations(int maxEvaluations);
+
+ /** Get the maximal number of functions evaluations.
+ * @return the maximal number of functions evaluations.
+ */
+ int getMaxEvaluations();
+
+ /** Get the number of evaluations of the objective function.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(UnivariateRealFunction, GoalType, double, double) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return the number of evaluations of the objective function.
+ */
+ int getEvaluations();
+
+ /**
+ * Find an optimum in the given interval.
+ * <p>
+ * An optimizer may require that the interval brackets a single optimum.
+ * </p>
+ * @param f the function to optimize.
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE}
+ * or {@link GoalType#MINIMIZE}.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @return a value where the function is optimum.
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the optimizer detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function.
+ * @throws IllegalArgumentException if min > max or the endpoints do not
+ * satisfy the requirements specified by the optimizer.
+ */
+ double optimize(UnivariateRealFunction f, GoalType goalType,
+ double min, double max)
+ throws ConvergenceException, FunctionEvaluationException;
+
+ /**
+ * Find an optimum in the given interval, start at startValue.
+ * <p>
+ * An optimizer may require that the interval brackets a single optimum.
+ * </p>
+ * @param f the function to optimize.
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE}
+ * or {@link GoalType#MINIMIZE}.
+ * @param min the lower bound for the interval.
+ * @param max the upper bound for the interval.
+ * @param startValue the start value to use.
+ * @return a value where the function is optimum.
+ * @throws ConvergenceException if the maximum iteration count is exceeded
+ * or the optimizer detects convergence problems otherwise.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function.
+ * @throws IllegalArgumentException if min > max or the arguments do not
+ * satisfy the requirements specified by the optimizer.
+ * @throws IllegalStateException if there are no data.
+ */
+ double optimize(UnivariateRealFunction f, GoalType goalType,
+ double min, double max, double startValue)
+ throws ConvergenceException, FunctionEvaluationException;
+
+ /**
+ * Get the result of the last run of the optimizer.
+ *
+ * @return the optimum.
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed.
+ */
+ double getResult();
+
+ /**
+ * Get the result of the last run of the optimizer.
+ *
+ * @return the value of the function at the optimum.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function.
+ * @throws IllegalStateException if there is no result available, either
+ * because no result was yet computed or the last attempt failed.
+ */
+ double getFunctionValue() throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/VectorialConvergenceChecker.java b/src/main/java/org/apache/commons/math/optimization/VectorialConvergenceChecker.java
new file mode 100644
index 0000000..e2befd6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/VectorialConvergenceChecker.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.math.optimization;
+
+/** This interface specifies how to check if a {@link
+ * DifferentiableMultivariateVectorialOptimizer optimization algorithm} has converged.
+ *
+ * <p>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.</p>
+ * <p>For convenience, two implementations that fit simple needs are already provided:
+ * {@link SimpleVectorialValueChecker} and {@link SimpleVectorialPointChecker}. The first
+ * one considers convergence is reached when the objective function value does not
+ * change much anymore, it does not use the point set at all. The second one
+ * considers convergence is reached when the input point set does not change
+ * much anymore, it does not use objective function value at all.</p>
+ *
+ * @version $Revision: 780674 $ $Date: 2009-06-01 17:10:55 +0200 (lun. 01 juin 2009) $
+ * @since 2.0
+ */
+
+public interface VectorialConvergenceChecker {
+
+ /** Check if the optimization algorithm has converged considering the last points.
+ * <p>
+ * 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.
+ * </p>
+ * @param iteration index of current iteration
+ * @param previous point from previous iteration
+ * @param current point from current iteration
+ * @return true if the algorithm is considered to have converged
+ */
+ boolean converged(int iteration, VectorialPointValuePair previous, VectorialPointValuePair current);
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/VectorialPointValuePair.java b/src/main/java/org/apache/commons/math/optimization/VectorialPointValuePair.java
new file mode 100644
index 0000000..13f1134
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/VectorialPointValuePair.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.math.optimization;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the vectorial value of an objective function at this point.
+ * <p>This is a simple immutable container.</p>
+ * @see RealPointValuePair
+ * @see org.apache.commons.math.analysis.MultivariateVectorialFunction
+ * @version $Revision: 980981 $ $Date: 2010-07-31 00:03:04 +0200 (sam. 31 juil. 2010) $
+ * @since 2.0
+ */
+public class VectorialPointValuePair implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1003888396256744753L;
+
+ /** Point coordinates. */
+ private final double[] point;
+
+ /** Vectorial value of the objective function at the point. */
+ private final double[] value;
+
+ /** Build a point/objective function value pair.
+ * @param point point coordinates (the built instance will store
+ * a copy of the array, not the array passed as argument)
+ * @param value value of an objective function at the point
+ */
+ public VectorialPointValuePair(final double[] point, final double[] value) {
+ this.point = (point == null) ? null : point.clone();
+ this.value = (value == null) ? null : value.clone();
+ }
+
+ /** Build a point/objective function value pair.
+ * @param point point coordinates (the built instance will store
+ * a copy of the array, not the array passed as argument)
+ * @param value value of an objective function at the point
+ * @param copyArray if true, the input arrays will be copied, otherwise
+ * they will be referenced
+ */
+ public VectorialPointValuePair(final double[] point, final double[] value,
+ final boolean copyArray) {
+ this.point = copyArray ?
+ ((point == null) ? null : point.clone()) :
+ point;
+ this.value = copyArray ?
+ ((value == null) ? null : value.clone()) :
+ value;
+ }
+
+ /** Get the point.
+ * @return a copy of the stored point
+ */
+ public double[] getPoint() {
+ return (point == null) ? null : point.clone();
+ }
+
+ /** Get a reference to the point.
+ * <p>This method is provided as a convenience to avoid copying
+ * the array, the elements of the array should <em>not</em> be modified.</p>
+ * @return a reference to the internal array storing the point
+ */
+ public double[] getPointRef() {
+ return point;
+ }
+
+ /** Get the value of the objective function.
+ * @return a copy of the stored value of the objective function
+ */
+ public double[] getValue() {
+ return (value == null) ? null : value.clone();
+ }
+
+ /** Get a reference to the value of the objective function.
+ * <p>This method is provided as a convenience to avoid copying
+ * the array, the elements of the array should <em>not</em> be modified.</p>
+ * @return a reference to the internal array storing the value of the objective function
+ */
+ public double[] getValueRef() {
+ return value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/direct/DirectSearchOptimizer.java b/src/main/java/org/apache/commons/math/optimization/direct/DirectSearchOptimizer.java
new file mode 100644
index 0000000..3f9fac2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/direct/DirectSearchOptimizer.java
@@ -0,0 +1,418 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.direct;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.MultivariateRealOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealConvergenceChecker;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+
+/**
+ * This class implements simplex-based direct search optimization
+ * algorithms.
+ *
+ * <p>Direct search methods only use objective function values, they don't
+ * 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 initial configuration of the simplex can be set using either
+ * {@link #setStartConfiguration(double[])} or {@link
+ * #setStartConfiguration(double[][])}. If neither method has been called
+ * before optimization is attempted, an explicit call to the first method
+ * with all steps set to +1 is triggered, thus building a default
+ * configuration from a unit hypercube. Each call to {@link
+ * #optimize(MultivariateRealFunction, GoalType, double[]) optimize} will reuse
+ * the current start configuration 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 start configuration will be reset to a default one with the
+ * appropriate dimensions.</p>
+ *
+ * <p>If {@link #setConvergenceChecker(RealConvergenceChecker)} is not called,
+ * a default {@link SimpleScalarValueChecker} is used.</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 class is the base class performing the boilerplate simplex
+ * initialization and handling. The simplex update by itself is
+ * performed by the derived classes according to the implemented
+ * algorithms.</p>
+ *
+ * implements MultivariateRealOptimizer since 2.0
+ *
+ * @see MultivariateRealFunction
+ * @see NelderMead
+ * @see MultiDirectional
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public abstract class DirectSearchOptimizer implements MultivariateRealOptimizer {
+
+ /** Simplex. */
+ protected RealPointValuePair[] simplex;
+
+ /** Objective function. */
+ private MultivariateRealFunction f;
+
+ /** Convergence checker. */
+ private RealConvergenceChecker checker;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int evaluations;
+
+ /** Start simplex configuration. */
+ private double[][] startConfiguration;
+
+ /** Simple constructor.
+ */
+ protected DirectSearchOptimizer() {
+ setConvergenceChecker(new SimpleScalarValueChecker());
+ setMaxIterations(Integer.MAX_VALUE);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** Set start configuration for simplex.
+ * <p>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.</p>
+ * <p>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).</p>
+ * @param steps steps along the canonical axes representing box edges,
+ * they may be negative but not null
+ * @exception IllegalArgumentException if one step is null
+ */
+ public void setStartConfiguration(final double[] steps)
+ throws IllegalArgumentException {
+ // only the relative position of the n final vertices with respect
+ // to the first one are stored
+ final int n = steps.length;
+ startConfiguration = new double[n][n];
+ for (int i = 0; i < n; ++i) {
+ final double[] vertexI = startConfiguration[i];
+ for (int j = 0; j < i + 1; ++j) {
+ if (steps[j] == 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX, j, j + 1);
+ }
+ System.arraycopy(steps, 0, vertexI, 0, j + 1);
+ }
+ }
+ }
+
+ /** Set start configuration for simplex.
+ * <p>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.</p>
+ * @param referenceSimplex reference simplex
+ * @exception IllegalArgumentException if the reference simplex does not
+ * contain at least one point, or if there is a dimension mismatch
+ * in the reference simplex or if one of its vertices is duplicated
+ */
+ public void setStartConfiguration(final double[][] referenceSimplex)
+ throws IllegalArgumentException {
+
+ // only the relative position of the n final vertices with respect
+ // to the first one are stored
+ final int n = referenceSimplex.length - 1;
+ if (n < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SIMPLEX_NEED_ONE_POINT);
+ }
+ startConfiguration = new double[n][n];
+ final double[] ref0 = referenceSimplex[0];
+
+ // vertices loop
+ for (int i = 0; i < n + 1; ++i) {
+
+ final double[] refI = referenceSimplex[i];
+
+ // safety checks
+ if (refI.length != n) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, refI.length, n);
+ }
+ for (int j = 0; j < i; ++j) {
+ final double[] refJ = referenceSimplex[j];
+ boolean allEquals = true;
+ for (int k = 0; k < n; ++k) {
+ if (refI[k] != refJ[k]) {
+ allEquals = false;
+ break;
+ }
+ }
+ if (allEquals) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 < n; ++k) {
+ confI[k] = refI[k] - ref0[k];
+ }
+ }
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return iterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(RealConvergenceChecker convergenceChecker) {
+ this.checker = convergenceChecker;
+ }
+
+ /** {@inheritDoc} */
+ public RealConvergenceChecker getConvergenceChecker() {
+ return checker;
+ }
+
+ /** {@inheritDoc} */
+ public RealPointValuePair optimize(final MultivariateRealFunction function,
+ final GoalType goalType,
+ final double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ if ((startConfiguration == null) ||
+ (startConfiguration.length != startPoint.length)) {
+ // no initial configuration has been set up for simplex
+ // build a default one from a unit hypercube
+ final double[] unit = new double[startPoint.length];
+ Arrays.fill(unit, 1.0);
+ setStartConfiguration(unit);
+ }
+
+ this.f = function;
+ final Comparator<RealPointValuePair> comparator =
+ new Comparator<RealPointValuePair>() {
+ public int compare(final RealPointValuePair o1,
+ final RealPointValuePair o2) {
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return (goalType == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ };
+
+ // initialize search
+ iterations = 0;
+ evaluations = 0;
+ buildSimplex(startPoint);
+ evaluateSimplex(comparator);
+
+ RealPointValuePair[] previous = new RealPointValuePair[simplex.length];
+ while (true) {
+
+ if (iterations > 0) {
+ boolean converged = true;
+ for (int i = 0; i < simplex.length; ++i) {
+ converged &= checker.converged(iterations, previous[i], simplex[i]);
+ }
+ if (converged) {
+ // we have found an optimum
+ return simplex[0];
+ }
+ }
+
+ // we still need to search
+ System.arraycopy(simplex, 0, previous, 0, simplex.length);
+ iterateSimplex(comparator);
+
+ }
+
+ }
+
+ /** Increment the iterations counter by 1.
+ * @exception OptimizationException if the maximal number
+ * of iterations is exceeded
+ */
+ protected void incrementIterationsCounter()
+ throws OptimizationException {
+ if (++iterations > maxIterations) {
+ throw new OptimizationException(new MaxIterationsExceededException(maxIterations));
+ }
+ }
+
+ /** Compute the next simplex of the algorithm.
+ * @param comparator comparator to use to sort simplex vertices from best to worst
+ * @exception FunctionEvaluationException if the function cannot be evaluated at
+ * some point
+ * @exception OptimizationException if the algorithm fails to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ protected abstract void iterateSimplex(final Comparator<RealPointValuePair> comparator)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+ /** Evaluate the objective function on one point.
+ * <p>A side effect of this method is to count the number of
+ * function evaluations</p>
+ * @param x point on which the objective function should be evaluated
+ * @return objective function value at the given point
+ * @exception FunctionEvaluationException if no value can be computed for the
+ * parameters or if the maximal number of evaluations is exceeded
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ protected double evaluate(final double[] x)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ if (++evaluations > maxEvaluations) {
+ throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations), x);
+ }
+ return f.value(x);
+ }
+
+ /** Build an initial simplex.
+ * @param startPoint the start point for optimization
+ * @exception IllegalArgumentException if the start point does not match
+ * simplex dimension
+ */
+ private void buildSimplex(final double[] startPoint)
+ throws IllegalArgumentException {
+
+ final int n = startPoint.length;
+ if (n != startConfiguration.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, n, startConfiguration.length);
+ }
+
+ // set first vertex
+ simplex = new RealPointValuePair[n + 1];
+ simplex[0] = new RealPointValuePair(startPoint, Double.NaN);
+
+ // set remaining vertices
+ for (int i = 0; i < n; ++i) {
+ final double[] confI = startConfiguration[i];
+ final double[] vertexI = new double[n];
+ for (int k = 0; k < n; ++k) {
+ vertexI[k] = startPoint[k] + confI[k];
+ }
+ simplex[i + 1] = new RealPointValuePair(vertexI, Double.NaN);
+ }
+
+ }
+
+ /** Evaluate all the non-evaluated points of the simplex.
+ * @param comparator comparator to use to sort simplex vertices from best to worst
+ * @exception FunctionEvaluationException if no value can be computed for the parameters
+ * @exception OptimizationException if the maximal number of evaluations is exceeded
+ */
+ protected void evaluateSimplex(final Comparator<RealPointValuePair> comparator)
+ throws FunctionEvaluationException, OptimizationException {
+
+ // evaluate the objective function at all non-evaluated simplex points
+ for (int i = 0; i < simplex.length; ++i) {
+ final RealPointValuePair vertex = simplex[i];
+ final double[] point = vertex.getPointRef();
+ if (Double.isNaN(vertex.getValue())) {
+ simplex[i] = new RealPointValuePair(point, evaluate(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 to sort simplex vertices from best to worst
+ */
+ protected void replaceWorstPoint(RealPointValuePair pointValuePair,
+ final Comparator<RealPointValuePair> comparator) {
+ int n = simplex.length - 1;
+ for (int i = 0; i < n; ++i) {
+ if (comparator.compare(simplex[i], pointValuePair) > 0) {
+ RealPointValuePair tmp = simplex[i];
+ simplex[i] = pointValuePair;
+ pointValuePair = tmp;
+ }
+ }
+ simplex[n] = pointValuePair;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/direct/MultiDirectional.java b/src/main/java/org/apache/commons/math/optimization/direct/MultiDirectional.java
new file mode 100644
index 0000000..ea8816b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/direct/MultiDirectional.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.direct;
+
+import java.util.Comparator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealConvergenceChecker;
+import org.apache.commons.math.optimization.RealPointValuePair;
+
+/**
+ * This class implements the multi-directional direct search method.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @see NelderMead
+ * @since 1.2
+ */
+public class MultiDirectional extends DirectSearchOptimizer {
+
+ /** Expansion coefficient. */
+ private final double khi;
+
+ /** Contraction coefficient. */
+ private final double gamma;
+
+ /** Build a multi-directional optimizer with default coefficients.
+ * <p>The default values are 2.0 for khi and 0.5 for gamma.</p>
+ */
+ public MultiDirectional() {
+ this.khi = 2.0;
+ this.gamma = 0.5;
+ }
+
+ /** Build a multi-directional optimizer with specified coefficients.
+ * @param khi expansion coefficient
+ * @param gamma contraction coefficient
+ */
+ public MultiDirectional(final double khi, final double gamma) {
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void iterateSimplex(final Comparator<RealPointValuePair> comparator)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ final RealConvergenceChecker checker = getConvergenceChecker();
+ while (true) {
+
+ incrementIterationsCounter();
+
+ // save the original vertex
+ final RealPointValuePair[] original = simplex;
+ final RealPointValuePair best = original[0];
+
+ // perform a reflection step
+ final RealPointValuePair reflected = evaluateNewSimplex(original, 1.0, comparator);
+ if (comparator.compare(reflected, best) < 0) {
+
+ // compute the expanded simplex
+ final RealPointValuePair[] reflectedSimplex = simplex;
+ final RealPointValuePair expanded = evaluateNewSimplex(original, khi, comparator);
+ if (comparator.compare(reflected, expanded) <= 0) {
+ // accept the reflected simplex
+ simplex = reflectedSimplex;
+ }
+
+ return;
+
+ }
+
+ // compute the contracted simplex
+ final RealPointValuePair contracted = evaluateNewSimplex(original, gamma, comparator);
+ if (comparator.compare(contracted, best) < 0) {
+ // accept the contracted simplex
+ return;
+ }
+
+ // check convergence
+ final int iter = getIterations();
+ boolean converged = true;
+ for (int i = 0; i < simplex.length; ++i) {
+ converged &= checker.converged(iter, original[i], simplex[i]);
+ }
+ if (converged) {
+ return;
+ }
+
+ }
+
+ }
+
+ /** Compute and evaluate a new simplex.
+ * @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 best point in the transformed simplex
+ * @exception FunctionEvaluationException if the function cannot be evaluated at some point
+ * @exception OptimizationException if the maximal number of evaluations is exceeded
+ */
+ private RealPointValuePair evaluateNewSimplex(final RealPointValuePair[] original,
+ final double coeff,
+ final Comparator<RealPointValuePair> comparator)
+ throws FunctionEvaluationException, OptimizationException {
+
+ final double[] xSmallest = original[0].getPointRef();
+ final int n = xSmallest.length;
+
+ // create the linearly transformed simplex
+ simplex = new RealPointValuePair[n + 1];
+ simplex[0] = original[0];
+ for (int i = 1; i <= n; ++i) {
+ final double[] xOriginal = original[i].getPointRef();
+ final double[] xTransformed = new double[n];
+ for (int j = 0; j < n; ++j) {
+ xTransformed[j] = xSmallest[j] + coeff * (xSmallest[j] - xOriginal[j]);
+ }
+ simplex[i] = new RealPointValuePair(xTransformed, Double.NaN, false);
+ }
+
+ // evaluate it
+ evaluateSimplex(comparator);
+ return simplex[0];
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/direct/NelderMead.java b/src/main/java/org/apache/commons/math/optimization/direct/NelderMead.java
new file mode 100644
index 0000000..8b8b205
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/direct/NelderMead.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.math.optimization.direct;
+
+import java.util.Comparator;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+
+/**
+ * This class implements the Nelder-Mead direct search method.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @see MultiDirectional
+ * @since 1.2
+ */
+public class NelderMead extends DirectSearchOptimizer {
+
+ /** 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 optimizer with default coefficients.
+ * <p>The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.</p>
+ */
+ public NelderMead() {
+ this.rho = 1.0;
+ this.khi = 2.0;
+ this.gamma = 0.5;
+ this.sigma = 0.5;
+ }
+
+ /** Build a Nelder-Mead optimizer with specified coefficients.
+ * @param rho reflection coefficient
+ * @param khi expansion coefficient
+ * @param gamma contraction coefficient
+ * @param sigma shrinkage coefficient
+ */
+ public NelderMead(final double rho, final double khi,
+ final double gamma, final double sigma) {
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void iterateSimplex(final Comparator<RealPointValuePair> comparator)
+ throws FunctionEvaluationException, OptimizationException {
+
+ incrementIterationsCounter();
+
+ // the simplex has n+1 point if dimension is n
+ final int n = simplex.length - 1;
+
+ // interesting values
+ final RealPointValuePair best = simplex[0];
+ final RealPointValuePair secondBest = simplex[n-1];
+ final RealPointValuePair worst = simplex[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 = simplex[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 RealPointValuePair reflected = new RealPointValuePair(xR, evaluate(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 RealPointValuePair expanded = new RealPointValuePair(xE, evaluate(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 RealPointValuePair outContracted = new RealPointValuePair(xC, evaluate(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 RealPointValuePair inContracted = new RealPointValuePair(xC, evaluate(xC), false);
+
+ if (comparator.compare(inContracted, worst) < 0) {
+ // accept the contraction point
+ replaceWorstPoint(inContracted, comparator);
+ return;
+ }
+
+ }
+
+ // perform a shrink
+ final double[] xSmallest = simplex[0].getPointRef();
+ for (int i = 1; i < simplex.length; ++i) {
+ final double[] x = simplex[i].getPoint();
+ for (int j = 0; j < n; ++j) {
+ x[j] = xSmallest[j] + sigma * (x[j] - xSmallest[j]);
+ }
+ simplex[i] = new RealPointValuePair(x, Double.NaN, false);
+ }
+ evaluateSimplex(comparator);
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/direct/PowellOptimizer.java b/src/main/java/org/apache/commons/math/optimization/direct/PowellOptimizer.java
new file mode 100644
index 0000000..6332951
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/direct/PowellOptimizer.java
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.direct;
+
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.general.AbstractScalarDifferentiableOptimizer;
+import org.apache.commons.math.optimization.univariate.AbstractUnivariateRealOptimizer;
+import org.apache.commons.math.optimization.univariate.BracketFinder;
+import org.apache.commons.math.optimization.univariate.BrentOptimizer;
+
+/**
+ * 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>).
+ *
+ * @version $Revision$ $Date$
+ * @since 2.2
+ */
+public class PowellOptimizer
+ extends AbstractScalarDifferentiableOptimizer {
+ /**
+ * Default relative tolerance for line search ({@value}).
+ */
+ public static final double DEFAULT_LS_RELATIVE_TOLERANCE = 1e-7;
+ /**
+ * Default absolute tolerance for line search ({@value}).
+ */
+ public static final double DEFAULT_LS_ABSOLUTE_TOLERANCE = 1e-11;
+ /**
+ * Line search.
+ */
+ private final LineSearch line;
+
+ /**
+ * Constructor with default line search tolerances (see the
+ * {@link #PowellOptimizer(double,double) other constructor}).
+ */
+ public PowellOptimizer() {
+ this(DEFAULT_LS_RELATIVE_TOLERANCE,
+ DEFAULT_LS_ABSOLUTE_TOLERANCE);
+ }
+
+ /**
+ * Constructor with default absolute line search tolerances (see
+ * the {@link #PowellOptimizer(double,double) other constructor}).
+ *
+ * @param lsRelativeTolerance Relative error tolerance for
+ * the line search algorithm ({@link BrentOptimizer}).
+ */
+ public PowellOptimizer(double lsRelativeTolerance) {
+ this(lsRelativeTolerance,
+ DEFAULT_LS_ABSOLUTE_TOLERANCE);
+ }
+
+ /**
+ * @param lsRelativeTolerance Relative error tolerance for
+ * the line search algorithm ({@link BrentOptimizer}).
+ * @param lsAbsoluteTolerance Relative error tolerance for
+ * the line search algorithm ({@link BrentOptimizer}).
+ */
+ public PowellOptimizer(double lsRelativeTolerance,
+ double lsAbsoluteTolerance) {
+ line = new LineSearch(lsRelativeTolerance,
+ lsAbsoluteTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected RealPointValuePair doOptimize()
+ throws FunctionEvaluationException,
+ OptimizationException {
+ final double[] guess = point.clone();
+ final int n = guess.length;
+
+ final double[][] direc = new double[n][n];
+ for (int i = 0; i < n; i++) {
+ direc[i][i] = 1;
+ }
+
+ double[] x = guess;
+ double fVal = computeObjectiveValue(x);
+ double[] x1 = x.clone();
+ while (true) {
+ incrementIterationsCounter();
+
+ 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 = /* Arrays.*/ copyOf(direc[i], n); // Java 1.5 does not support Arrays.copyOf()
+
+ fX2 = fVal;
+
+ line.search(x, d);
+ fVal = line.getValueAtOptimum();
+ alphaMin = line.getOptimum();
+ final double[][] result = newPointAndDirection(x, d, alphaMin);
+ x = result[0];
+
+ if ((fX2 - fVal) > delta) {
+ delta = fX2 - fVal;
+ bigInd = i;
+ }
+ }
+
+ final RealPointValuePair previous = new RealPointValuePair(x1, fX);
+ final RealPointValuePair current = new RealPointValuePair(x, fVal);
+ if (getConvergenceChecker().converged(getIterations(), previous, current)) {
+ 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) {
+ line.search(x, d);
+ fVal = line.getValueAtOptimum();
+ alphaMin = line.getOptimum();
+ 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.
+ * The parameters {@code p} and {@code d} will be changed in-place.
+ *
+ * @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[][] result = new double[2][n];
+ final double[] nP = result[0];
+ final double[] nD = result[1];
+ for (int i = 0; i < n; i++) {
+ nD[i] = d[i] * optimum;
+ nP[i] = p[i] + nD[i];
+ }
+ return result;
+ }
+
+ /**
+ * Class for finding the minimum of the objective function along a given
+ * direction.
+ */
+ private class LineSearch {
+ /**
+ * Optimizer.
+ */
+ private final AbstractUnivariateRealOptimizer optim = new BrentOptimizer();
+ /**
+ * Automatic bracketing.
+ */
+ private final BracketFinder bracket = new BracketFinder();
+ /**
+ * Value of the optimum.
+ */
+ private double optimum = Double.NaN;
+ /**
+ * Value of the objective function at the optimum.
+ */
+ private double valueAtOptimum = Double.NaN;
+
+ /**
+ * @param relativeTolerance Relative tolerance.
+ * @param absoluteTolerance Absolute tolerance.
+ */
+ public LineSearch(double relativeTolerance,
+ double absoluteTolerance) {
+ optim.setRelativeAccuracy(relativeTolerance);
+ optim.setAbsoluteAccuracy(absoluteTolerance);
+ }
+
+ /**
+ * Find the minimum of the function {@code f(p + alpha * d)}.
+ *
+ * @param p Starting point.
+ * @param d Search direction.
+ * @throws FunctionEvaluationException if function cannot be evaluated at some test point
+ * @throws OptimizationException if algorithm fails to converge
+ */
+ public void search(final double[] p, final double[] d)
+ throws OptimizationException, FunctionEvaluationException {
+
+ // Reset.
+ optimum = Double.NaN;
+ valueAtOptimum = Double.NaN;
+
+ try {
+ final int n = p.length;
+ final UnivariateRealFunction f = new UnivariateRealFunction() {
+ public double value(double alpha)
+ throws FunctionEvaluationException {
+
+ final double[] x = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = p[i] + alpha * d[i];
+ }
+ final double obj;
+ obj = computeObjectiveValue(x);
+ return obj;
+ }
+ };
+
+ bracket.search(f, goal, 0, 1);
+ optimum = optim.optimize(f, goal,
+ bracket.getLo(),
+ bracket.getHi(),
+ bracket.getMid());
+ valueAtOptimum = optim.getFunctionValue();
+ } catch (MaxIterationsExceededException e) {
+ throw new OptimizationException(e);
+ }
+ }
+
+ /**
+ * @return the optimum.
+ */
+ public double getOptimum() {
+ return optimum;
+ }
+ /**
+ * @return the value of the function at the optimum.
+ */
+ public double getValueAtOptimum() {
+ return valueAtOptimum;
+ }
+ }
+
+ /**
+ * Java 1.5 does not support Arrays.copyOf()
+ *
+ * @param source the array to be copied
+ * @param newLen the length of the copy to be returned
+ * @return the copied array, truncated or padded as necessary.
+ */
+ private double[] copyOf(double[] source, int newLen) {
+ double[] output = new double[newLen];
+ System.arraycopy(source, 0, output, 0, Math.min(source.length, newLen));
+ return output;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/direct/package.html b/src/main/java/org/apache/commons/math/optimization/direct/package.html
new file mode 100644
index 0000000..8e9c48e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/direct/package.html
@@ -0,0 +1,24 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 748274 $ -->
+<body>
+<p>
+This package provides optimization algorithms that don't require derivatives.
+</p>
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/CurveFitter.java b/src/main/java/org/apache/commons/math/optimization/fitting/CurveFitter.java
new file mode 100644
index 0000000..9bb70d1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/CurveFitter.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.math.optimization.fitting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction;
+import org.apache.commons.math.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+
+/** Fitter for parametric univariate real functions y = f(x).
+ * <p>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.</p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public class CurveFitter {
+
+ /** Optimizer to use for the fitting. */
+ private final DifferentiableMultivariateVectorialOptimizer optimizer;
+
+ /** Observed points. */
+ private final List<WeightedObservedPoint> observations;
+
+ /** Simple constructor.
+ * @param optimizer optimizer to use for the fitting
+ */
+ public CurveFitter(final DifferentiableMultivariateVectorialOptimizer 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)</code>.</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.
+ * <p>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.</p>
+ * @param f parametric function to fit
+ * @param initialGuess first guess of the function parameters
+ * @return fitted parameters
+ * @exception FunctionEvaluationException if the objective function throws one during the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ public double[] fit(final ParametricRealFunction f,
+ final double[] initialGuess)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ // 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
+ VectorialPointValuePair optimum =
+ optimizer.optimize(new TheoreticalValuesFunction(f), target, weights, initialGuess);
+
+ // extract the coefficients
+ return optimum.getPointRef();
+
+ }
+
+ /** Vectorial function computing function theoretical values. */
+ private class TheoreticalValuesFunction
+ implements DifferentiableMultivariateVectorialFunction {
+
+ /** Function to fit. */
+ private final ParametricRealFunction f;
+
+ /** Simple constructor.
+ * @param f function to fit.
+ */
+ public TheoreticalValuesFunction(final ParametricRealFunction f) {
+ this.f = f;
+ }
+
+ /** {@inheritDoc} */
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ public double[][] value(double[] point)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ 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) throws FunctionEvaluationException, IllegalArgumentException {
+
+ // 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;
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/GaussianDerivativeFunction.java b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianDerivativeFunction.java
new file mode 100644
index 0000000..7112d17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianDerivativeFunction.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.math.optimization.fitting;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.ZeroException;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * The derivative of {@link GaussianFunction}. Specifically:
+ * <p>
+ * <tt>f'(x) = (-b / (d^2)) * (x - c) * exp(-((x - c)^2) / (2*(d^2)))</tt>
+ * <p>
+ * Notation key:
+ * <ul>
+ * <li><tt>x^n</tt>: <tt>x</tt> raised to the power of <tt>n</tt>
+ * <li><tt>exp(x)</tt>: <i>e</i><tt>^x</tt>
+ * </ul>
+ *
+ * @since 2.2
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class GaussianDerivativeFunction implements UnivariateRealFunction, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6500229089670174766L;
+
+ /** Parameter b of this function. */
+ private final double b;
+
+ /** Parameter c of this function. */
+ private final double c;
+
+ /** Square of the parameter d of this function. */
+ private final double d2;
+
+ /**
+ * Constructs an instance with the specified parameters.
+ *
+ * @param b <tt>b</tt> parameter value
+ * @param c <tt>c</tt> parameter value
+ * @param d <tt>d</tt> parameter value
+ *
+ * @throws IllegalArgumentException if <code>d</code> is 0
+ */
+ public GaussianDerivativeFunction(double b, double c, double d) {
+ if (d == 0.0) {
+ throw new ZeroException();
+ }
+ this.b = b;
+ this.c = c;
+ this.d2 = d * d;
+ }
+
+ /**
+ * Constructs an instance with the specified parameters.
+ *
+ * @param parameters <tt>b</tt>, <tt>c</tt>, and <tt>d</tt> parameter values
+ *
+ * @throws IllegalArgumentException if <code>parameters</code> is null,
+ * <code>parameters</code> length is not 3, or if
+ * <code>parameters[2]</code> is 0
+ */
+ public GaussianDerivativeFunction(double[] parameters) {
+ if (parameters == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (parameters.length != 3) {
+ throw new DimensionMismatchException(3, parameters.length);
+ }
+ if (parameters[2] == 0.0) {
+ throw new ZeroException();
+ }
+ this.b = parameters[0];
+ this.c = parameters[1];
+ this.d2 = parameters[2] * parameters[2];
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ final double xMc = x - c;
+ return (-b / d2) * xMc * Math.exp(-(xMc * xMc) / (2.0 * d2));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFitter.java b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFitter.java
new file mode 100644
index 0000000..6bc9f22
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFitter.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.math.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+import org.apache.commons.math.optimization.fitting.WeightedObservedPoint;
+
+/**
+ * Fits points to a Gaussian function (that is, a {@link GaussianFunction}).
+ * <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);
+ * GaussianFunction fitFunction = fitter.fit();
+ * </pre>
+ *
+ * @see ParametricGaussianFunction
+ * @since 2.2
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class GaussianFitter {
+
+ /** Fitter used for fitting. */
+ private final CurveFitter fitter;
+
+ /**
+ * Constructs an instance using the specified optimizer.
+ *
+ * @param optimizer optimizer to use for the fitting
+ */
+ public GaussianFitter(DifferentiableMultivariateVectorialOptimizer optimizer) {
+ fitter = new CurveFitter(optimizer);
+ }
+
+ /**
+ * Adds point (<code>x</code>, <code>y</code>) to list of observed points
+ * with a weight of 1.0.
+ *
+ * @param x <tt>x</tt> point value
+ * @param y <tt>y</tt> point value
+ */
+ public void addObservedPoint(double x, double y) {
+ addObservedPoint(1.0, x, y);
+ }
+
+ /**
+ * Adds point (<code>x</code>, <code>y</code>) to list of observed points
+ * with a weight of <code>weight</code>.
+ *
+ * @param weight weight assigned to point
+ * @param x <tt>x</tt> point value
+ * @param y <tt>y</tt> point value
+ */
+ public void addObservedPoint(double weight, double x, double y) {
+ fitter.addObservedPoint(weight, x, y);
+ }
+
+ /**
+ * Fits Gaussian function to the observed points.
+ *
+ * @return Gaussian function best fitting the observed points
+ *
+ * @throws FunctionEvaluationException if <code>CurveFitter.fit</code> throws it
+ * @throws OptimizationException if <code>CurveFitter.fit</code> throws it
+ * @throws IllegalArgumentException if <code>CurveFitter.fit</code> throws it
+ *
+ * @see CurveFitter
+ */
+ public GaussianFunction fit() throws FunctionEvaluationException, OptimizationException {
+ return new GaussianFunction(fitter.fit(new ParametricGaussianFunction(),
+ createParametersGuesser(fitter.getObservations()).guess()));
+ }
+
+ /**
+ * Factory method to create a <code>GaussianParametersGuesser</code>
+ * instance initialized with the specified observations.
+ *
+ * @param observations points used to initialize the created
+ * <code>GaussianParametersGuesser</code> instance
+ *
+ * @return new <code>GaussianParametersGuesser</code> instance
+ */
+ protected GaussianParametersGuesser createParametersGuesser(WeightedObservedPoint[] observations) {
+ return new GaussianParametersGuesser(observations);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFunction.java b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFunction.java
new file mode 100644
index 0000000..06a0391
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianFunction.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.math.optimization.fitting;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.ZeroException;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * A Gaussian function. Specifically:
+ * <p>
+ * <tt>f(x) = a + b*exp(-((x - c)^2 / (2*d^2)))</tt>
+ * <p>
+ * Notation key:
+ * <ul>
+ * <li><tt>x^n</tt>: <tt>x</tt> raised to the power of <tt>n</tt>
+ * <li><tt>exp(x)</tt>: <i>e</i><tt>^x</tt>
+ * </ul>
+ * References:
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Gaussian_function">Wikipedia:
+ * Gaussian function</a>
+ * </ul>
+ *
+ * @see GaussianDerivativeFunction
+ * @see ParametricGaussianFunction
+ * @since 2.2
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class GaussianFunction implements DifferentiableUnivariateRealFunction, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3195385616125629512L;
+
+ /** Parameter a of this function. */
+ private final double a;
+
+ /** Parameter b of this function. */
+ private final double b;
+
+ /** Parameter c of this function. */
+ private final double c;
+
+ /** Parameter d of this function. */
+ private final double d;
+
+ /**
+ * Constructs an instance with the specified parameters.
+ *
+ * @param a <tt>a</tt> parameter value
+ * @param b <tt>b</tt> parameter value
+ * @param c <tt>c</tt> parameter value
+ * @param d <tt>d</tt> parameter value
+ *
+ * @throws IllegalArgumentException if <code>d</code> is 0
+ */
+ public GaussianFunction(double a, double b, double c, double d) {
+ if (d == 0.0) {
+ throw new ZeroException();
+ }
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ }
+
+ /**
+ * Constructs an instance with the specified parameters.
+ *
+ * @param parameters <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and <tt>d</tt>
+ * parameter values
+ *
+ * @throws IllegalArgumentException if <code>parameters</code> is null,
+ * <code>parameters</code> length is not 4, or if
+ * <code>parameters[3]</code> is 0
+ */
+ public GaussianFunction(double[] parameters) {
+ if (parameters == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (parameters.length != 4) {
+ throw new DimensionMismatchException(4, parameters.length);
+ }
+ if (parameters[3] == 0.0) {
+ throw new ZeroException();
+ }
+ this.a = parameters[0];
+ this.b = parameters[1];
+ this.c = parameters[2];
+ this.d = parameters[3];
+ }
+
+ /** {@inheritDoc} */
+ public UnivariateRealFunction derivative() {
+ return new GaussianDerivativeFunction(b, c, d);
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ final double xMc = x - c;
+ return a + b * Math.exp(-xMc * xMc / (2.0 * (d * d)));
+ }
+
+ /**
+ * Gets <tt>a</tt> parameter value.
+ *
+ * @return <tt>a</tt> parameter value
+ */
+ public double getA() {
+ return a;
+ }
+
+ /**
+ * Gets <tt>b</tt> parameter value.
+ *
+ * @return <tt>b</tt> parameter value
+ */
+ public double getB() {
+ return b;
+ }
+
+ /**
+ * Gets <tt>c</tt> parameter value.
+ *
+ * @return <tt>c</tt> parameter value
+ */
+ public double getC() {
+ return c;
+ }
+
+ /**
+ * Gets <tt>d</tt> parameter value.
+ *
+ * @return <tt>d</tt> parameter value
+ */
+ public double getD() {
+ return d;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/GaussianParametersGuesser.java b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianParametersGuesser.java
new file mode 100644
index 0000000..3fb3698
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/GaussianParametersGuesser.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.fitting;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.exception.ZeroException;
+import org.apache.commons.math.exception.NullArgumentException;
+
+/**
+ * Guesses the parameters ({@code a}, {@code b}, {@code c}, and {@code d})
+ * of a {@link ParametricGaussianFunction} based on the specified observed
+ * points.
+ *
+ * @since 2.2
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class GaussianParametersGuesser {
+
+ /** Observed points. */
+ private final WeightedObservedPoint[] observations;
+
+ /** Resulting guessed parameters. */
+ private double[] parameters;
+
+ /**
+ * Constructs instance with the specified observed points.
+ *
+ * @param observations observed points upon which should base guess
+ */
+ public GaussianParametersGuesser(WeightedObservedPoint[] observations) {
+ if (observations == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (observations.length < 3) {
+ throw new NumberIsTooSmallException(observations.length, 3, true);
+ }
+ this.observations = observations.clone();
+ }
+
+ /**
+ * Guesses the parameters based on the observed points.
+ *
+ * @return guessed parameters array <code>{a, b, c, d}</code>
+ */
+ public double[] guess() {
+ if (parameters == null) {
+ parameters = basicGuess(observations);
+ }
+ return parameters.clone();
+ }
+
+ /**
+ * Guesses the parameters based on the specified observed points.
+ *
+ * @param points observed points upon which should base guess
+ *
+ * @return guessed parameters array <code>{a, b, c, d}</code>
+ */
+ private double[] basicGuess(WeightedObservedPoint[] points) {
+ Arrays.sort(points, createWeightedObservedPointComparator());
+ double[] params = new double[4];
+
+ int minYIdx = findMinY(points);
+ params[0] = points[minYIdx].getY();
+
+ int maxYIdx = findMaxY(points);
+ params[1] = points[maxYIdx].getY();
+ params[2] = points[maxYIdx].getX();
+
+ double fwhmApprox;
+ try {
+ double halfY = params[0] + ((params[1] - params[0]) / 2.0);
+ double fwhmX1 = interpolateXAtY(points, maxYIdx, -1, halfY);
+ double fwhmX2 = interpolateXAtY(points, maxYIdx, +1, halfY);
+ fwhmApprox = fwhmX2 - fwhmX1;
+ } catch (OutOfRangeException e) {
+ fwhmApprox = points[points.length - 1].getX() - points[0].getX();
+ }
+ params[3] = fwhmApprox / (2.0 * Math.sqrt(2.0 * Math.log(2.0)));
+
+ return params;
+ }
+
+ /**
+ * Finds index of point in specified points with the smallest Y.
+ *
+ * @param points points to search
+ *
+ * @return index in specified points array
+ */
+ private int findMinY(WeightedObservedPoint[] points) {
+ int minYIdx = 0;
+ for (int i = 1; i < points.length; i++) {
+ if (points[i].getY() < points[minYIdx].getY()) {
+ minYIdx = i;
+ }
+ }
+ return minYIdx;
+ }
+
+ /**
+ * Finds index of point in specified points with the largest Y.
+ *
+ * @param points points to search
+ *
+ * @return 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 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 value of X at the specified Y
+ *
+ * @throws IllegalArgumentException if idxStep is 0
+ * @throws OutOfRangeException if specified <code>y</code> is not within the
+ * range of the specified <code>points</code>
+ */
+ private double interpolateXAtY(WeightedObservedPoint[] points,
+ int startIdx, int idxStep, double y) throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ WeightedObservedPoint[] twoPoints = getInterpolationPointsForY(points, startIdx, idxStep, y);
+ WeightedObservedPoint pointA = twoPoints[0];
+ WeightedObservedPoint pointB = twoPoints[1];
+ if (pointA.getY() == y) {
+ return pointA.getX();
+ }
+ if (pointB.getY() == y) {
+ return pointB.getX();
+ }
+ return pointA.getX() +
+ (((y - pointA.getY()) * (pointB.getX() - pointA.getX())) / (pointB.getY() - pointA.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 array containing two points suitable for determining X at the
+ * specified Y
+ *
+ * @throws IllegalArgumentException if idxStep is 0
+ * @throws OutOfRangeException if specified <code>y</code> is not within the
+ * range of the specified <code>points</code>
+ */
+ 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) {
+ if (isBetween(y, points[i].getY(), points[i + idxStep].getY())) {
+ return (idxStep < 0) ?
+ new WeightedObservedPoint[] { points[i + idxStep], points[i] } :
+ new WeightedObservedPoint[] { points[i], points[i + idxStep] };
+ }
+ }
+
+ double minY = Double.POSITIVE_INFINITY;
+ double maxY = Double.NEGATIVE_INFINITY;
+ for (final WeightedObservedPoint point : points) {
+ minY = Math.min(minY, point.getY());
+ maxY = Math.max(maxY, point.getY());
+ }
+ throw new OutOfRangeException(y, minY, maxY);
+
+ }
+
+ /**
+ * Determines whether a value is between two other values.
+ *
+ * @param value value to determine whether is between <code>boundary1</code>
+ * and <code>boundary2</code>
+ * @param boundary1 one end of the range
+ * @param boundary2 other end of the range
+ *
+ * @return true if <code>value</code> is between <code>boundary1</code> and
+ * <code>boundary2</code> (inclusive); false otherwise
+ */
+ private boolean isBetween(double value, double boundary1, double boundary2) {
+ return (value >= boundary1 && value <= boundary2) ||
+ (value >= boundary2 && value <= boundary1);
+ }
+
+ /**
+ * Factory method creating <code>Comparator</code> for comparing
+ * <code>WeightedObservedPoint</code> instances.
+ *
+ * @return new <code>Comparator</code> instance
+ */
+ private Comparator<WeightedObservedPoint> createWeightedObservedPointComparator() {
+ return new Comparator<WeightedObservedPoint>() {
+ public int compare(WeightedObservedPoint p1, WeightedObservedPoint p2) {
+ if (p1 == null && p2 == null) {
+ return 0;
+ }
+ if (p1 == null) {
+ return -1;
+ }
+ if (p2 == null) {
+ return 1;
+ }
+ if (p1.getX() < p2.getX()) {
+ return -1;
+ }
+ if (p1.getX() > p2.getX()) {
+ return 1;
+ }
+ if (p1.getY() < p2.getY()) {
+ return -1;
+ }
+ if (p1.getY() > p2.getY()) {
+ return 1;
+ }
+ if (p1.getWeight() < p2.getWeight()) {
+ return -1;
+ }
+ if (p1.getWeight() > p2.getWeight()) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicCoefficientsGuesser.java b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicCoefficientsGuesser.java
new file mode 100644
index 0000000..0796f67
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicCoefficientsGuesser.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.math.optimization.fitting;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.util.FastMath;
+
+/** 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>
+
+ * @version $Revision: 1056034 $ $Date: 2011-01-06 20:41:43 +0100 (jeu. 06 janv. 2011) $
+ * @since 2.0
+
+ */
+public class HarmonicCoefficientsGuesser {
+
+ /** Sampled observations. */
+ private final WeightedObservedPoint[] observations;
+
+ /** Guessed amplitude. */
+ private double a;
+
+ /** Guessed pulsation &omega;. */
+ private double omega;
+
+ /** Guessed phase &phi;. */
+ private double phi;
+
+ /** Simple constructor.
+ * @param observations sampled observations
+ */
+ public HarmonicCoefficientsGuesser(WeightedObservedPoint[] observations) {
+ this.observations = observations.clone();
+ a = Double.NaN;
+ omega = Double.NaN;
+ }
+
+ /** Estimate a first guess of the coefficients.
+ * @exception OptimizationException if the sample is too short or if
+ * the first guess cannot be computed (when the elements under the
+ * square roots are negative).
+ * */
+ public void guess() throws OptimizationException {
+ sortObservations();
+ guessAOmega();
+ guessPhi();
+ }
+
+ /** Sort the observations with respect to the abscissa.
+ */
+ private void sortObservations() {
+
+ // 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];
+ }
+ }
+
+ }
+
+ /** Estimate a first guess of the a and &omega; coefficients.
+ * @exception OptimizationException if the sample is too short or if
+ * the first guess cannot be computed (when the elements under the
+ * square roots are negative).
+ */
+ private void guessAOmega() throws OptimizationException {
+
+ // initialize the sums for the linear model between the two integrals
+ double sx2 = 0.0;
+ double sy2 = 0.0;
+ double sxy = 0.0;
+ double sxz = 0.0;
+ double syz = 0.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.0) || (c2 / c3 < 0.0)) {
+ throw new OptimizationException(LocalizedFormats.UNABLE_TO_FIRST_GUESS_HARMONIC_COEFFICIENTS);
+ }
+ a = FastMath.sqrt(c1 / c2);
+ omega = FastMath.sqrt(c2 / c3);
+
+ }
+
+ /** Estimate a first guess of the &phi; coefficient.
+ */
+ private void guessPhi() {
+
+ // initialize the means
+ double fcMean = 0.0;
+ double fsMean = 0.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;
+
+ }
+
+ phi = FastMath.atan2(-fsMean, fcMean);
+
+ }
+
+ /** Get the guessed amplitude a.
+ * @return guessed amplitude a;
+ */
+ public double getGuessedAmplitude() {
+ return a;
+ }
+
+ /** Get the guessed pulsation &omega;.
+ * @return guessed pulsation &omega;
+ */
+ public double getGuessedPulsation() {
+ return omega;
+ }
+
+ /** Get the guessed phase &phi;.
+ * @return guessed phase &phi;
+ */
+ public double getGuessedPhase() {
+ return phi;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFitter.java b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFitter.java
new file mode 100644
index 0000000..ef019c9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFitter.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.util.FastMath;
+
+/** This class 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.</p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ */
+public class HarmonicFitter {
+
+ /** Fitter for the coefficients. */
+ private final CurveFitter fitter;
+
+ /** Values for amplitude, pulsation &omega; and phase &phi;. */
+ private double[] parameters;
+
+ /** Simple constructor.
+ * @param optimizer optimizer to use for the fitting
+ */
+ public HarmonicFitter(final DifferentiableMultivariateVectorialOptimizer optimizer) {
+ this.fitter = new CurveFitter(optimizer);
+ parameters = null;
+ }
+
+ /** Simple constructor.
+ * <p>This constructor can be used when a first guess of the
+ * coefficients is already known.</p>
+ * @param optimizer optimizer to use for the fitting
+ * @param initialGuess guessed values for amplitude (index 0),
+ * pulsation &omega; (index 1) and phase &phi; (index 2)
+ */
+ public HarmonicFitter(final DifferentiableMultivariateVectorialOptimizer optimizer,
+ final double[] initialGuess) {
+ this.fitter = new CurveFitter(optimizer);
+ this.parameters = initialGuess.clone();
+ }
+
+ /** 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 P(x) as close as possible to this value
+ */
+ public void addObservedPoint(double weight, double x, double y) {
+ fitter.addObservedPoint(weight, x, y);
+ }
+
+ /** Fit an harmonic function to the observed points.
+ * @return harmonic function best fitting the observed points
+ * @throws OptimizationException if the sample is too short or if
+ * the first guess cannot be computed
+ */
+ public HarmonicFunction fit() throws OptimizationException {
+
+ // shall we compute the first guess of the parameters ourselves ?
+ if (parameters == null) {
+ final WeightedObservedPoint[] observations = fitter.getObservations();
+ if (observations.length < 4) {
+ throw new OptimizationException(LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE,
+ observations.length, 4);
+ }
+
+ HarmonicCoefficientsGuesser guesser = new HarmonicCoefficientsGuesser(observations);
+ guesser.guess();
+ parameters = new double[] {
+ guesser.getGuessedAmplitude(),
+ guesser.getGuessedPulsation(),
+ guesser.getGuessedPhase()
+ };
+
+ }
+
+ try {
+ double[] fitted = fitter.fit(new ParametricHarmonicFunction(), parameters);
+ return new HarmonicFunction(fitted[0], fitted[1], fitted[2]);
+ } catch (FunctionEvaluationException fee) {
+ // should never happen
+ throw new RuntimeException(fee);
+ }
+
+ }
+
+ /** Parametric harmonic function. */
+ private static class ParametricHarmonicFunction implements ParametricRealFunction {
+
+ /** {@inheritDoc} */
+ public double value(double x, double[] parameters) {
+ final double a = parameters[0];
+ final double omega = parameters[1];
+ final double phi = parameters[2];
+ return a * FastMath.cos(omega * x + phi);
+ }
+
+ /** {@inheritDoc} */
+ public double[] gradient(double x, double[] parameters) {
+ final double a = parameters[0];
+ final double omega = parameters[1];
+ final double phi = parameters[2];
+ final double alpha = omega * x + phi;
+ final double cosAlpha = FastMath.cos(alpha);
+ final double sinAlpha = FastMath.sin(alpha);
+ return new double[] { cosAlpha, -a * x * sinAlpha, -a * sinAlpha };
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFunction.java b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFunction.java
new file mode 100644
index 0000000..cce3533
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/HarmonicFunction.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.math.optimization.fitting;
+
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+/** Harmonic function of the form <code>f (t) = a cos (&omega; t + &phi;)</code>.
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+ */
+public class HarmonicFunction implements DifferentiableUnivariateRealFunction {
+
+ /** Amplitude a. */
+ private final double a;
+
+ /** Pulsation &omega;. */
+ private final double omega;
+
+ /** Phase &phi;. */
+ private final double phi;
+
+ /** Simple constructor.
+ * @param a amplitude
+ * @param omega pulsation
+ * @param phi phase
+ */
+ public HarmonicFunction(double a, double omega, double phi) {
+ this.a = a;
+ this.omega = omega;
+ this.phi = phi;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return a * FastMath.cos(omega * x + phi);
+ }
+
+ /** {@inheritDoc} */
+ public HarmonicFunction derivative() {
+ return new HarmonicFunction(a * omega, omega, phi + FastMath.PI / 2);
+ }
+
+ /** Get the amplitude a.
+ * @return amplitude a;
+ */
+ public double getAmplitude() {
+ return a;
+ }
+
+ /** Get the pulsation &omega;.
+ * @return pulsation &omega;
+ */
+ public double getPulsation() {
+ return omega;
+ }
+
+ /** Get the phase &phi;.
+ * @return phase &phi;
+ */
+ public double getPhase() {
+ return phi;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunction.java b/src/main/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunction.java
new file mode 100644
index 0000000..9deb731
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunction.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.math.optimization.fitting;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.ZeroException;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.optimization.fitting.ParametricRealFunction;
+
+/**
+ * A Gaussian function. Specifically:
+ * <p>
+ * <tt>f(x) = a + b*exp(-((x - c)^2 / (2*d^2)))</tt>
+ * <p>
+ * The parameters have the following meaning:
+ * <ul>
+ * <li><tt>a</tt> is a constant offset that shifts <tt>f(x)</tt> up or down
+ * <li><tt>b</tt> is the height of the peak
+ * <li><tt>c</tt> is the position of the center of the peak
+ * <li><tt>d</tt> is related to the FWHM by <tt>FWHM = 2*sqrt(2*ln(2))*d</tt>
+ * </ul>
+ * Notation key:
+ * <ul>
+ * <li><tt>x^n</tt>: <tt>x</tt> raised to the power of <tt>n</tt>
+ * <li><tt>exp(x)</tt>: <i>e</i><tt>^x</tt>
+ * <li><tt>sqrt(x)</tt>: the square root of <tt>x</tt>
+ * <li><tt>ln(x)</tt>: the natural logarithm of <tt>x</tt>
+ * </ul>
+ * References:
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Gaussian_function">Wikipedia:
+ * Gaussian function</a>
+ * </ul>
+ *
+ * @since 2.2
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class ParametricGaussianFunction implements ParametricRealFunction, Serializable {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -3875578602503903233L;
+
+ /**
+ * Constructs an instance.
+ */
+ public ParametricGaussianFunction() {
+ }
+
+ /**
+ * Computes value of function <tt>f(x)</tt> for the specified <tt>x</tt> and
+ * parameters <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and <tt>d</tt>.
+ *
+ * @param x <tt>x</tt> value
+ * @param parameters values of <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and
+ * <tt>d</tt>
+ *
+ * @return value of <tt>f(x)</tt> evaluated at <tt>x</tt> with the specified
+ * parameters
+ *
+ * @throws IllegalArgumentException if <code>parameters</code> is invalid as
+ * determined by {@link #validateParameters(double[])}
+ * @throws ZeroException if <code>parameters</code> values are
+ * invalid as determined by {@link #validateParameters(double[])}
+ */
+ public double value(double x, double[] parameters) throws ZeroException {
+ validateParameters(parameters);
+ final double a = parameters[0];
+ final double b = parameters[1];
+ final double c = parameters[2];
+ final double d = parameters[3];
+ final double xMc = x - c;
+ return a + b * Math.exp(-xMc * xMc / (2.0 * (d * d)));
+ }
+
+ /**
+ * Computes the gradient vector for a four variable version of the function
+ * where the parameters, <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and <tt>d</tt>,
+ * are considered the variables, not <tt>x</tt>. That is, instead of
+ * computing the gradient vector for the function <tt>f(x)</tt> (which would
+ * just be the derivative of <tt>f(x)</tt> with respect to <tt>x</tt> since
+ * it's a one-dimensional function), computes the gradient vector for the
+ * function <tt>f(a, b, c, d) = a + b*exp(-((x - c)^2 / (2*d^2)))</tt>
+ * treating the specified <tt>x</tt> as a constant.
+ * <p>
+ * The components of the computed gradient vector are the partial
+ * derivatives of <tt>f(a, b, c, d)</tt> with respect to each variable.
+ * That is, the partial derivative of <tt>f(a, b, c, d)</tt> with respect to
+ * <tt>a</tt>, the partial derivative of <tt>f(a, b, c, d)</tt> with respect
+ * to <tt>b</tt>, the partial derivative of <tt>f(a, b, c, d)</tt> with
+ * respect to <tt>c</tt>, and the partial derivative of <tt>f(a, b, c,
+ * d)</tt> with respect to <tt>d</tt>.
+ *
+ * @param x <tt>x</tt> value to be used as constant in <tt>f(a, b, c,
+ * d)</tt>
+ * @param parameters values of <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and
+ * <tt>d</tt> for computation of gradient vector of <tt>f(a, b, c,
+ * d)</tt>
+ *
+ * @return gradient vector of <tt>f(a, b, c, d)</tt>
+ *
+ * @throws IllegalArgumentException if <code>parameters</code> is invalid as
+ * determined by {@link #validateParameters(double[])}
+ * @throws ZeroException if <code>parameters</code> values are
+ * invalid as determined by {@link #validateParameters(double[])}
+ */
+ public double[] gradient(double x, double[] parameters) throws ZeroException {
+
+ validateParameters(parameters);
+ final double b = parameters[1];
+ final double c = parameters[2];
+ final double d = parameters[3];
+
+ final double xMc = x - c;
+ final double d2 = d * d;
+ final double exp = Math.exp(-xMc * xMc / (2 * d2));
+ final double f = b * exp * xMc / d2;
+
+ return new double[] { 1.0, exp, f, f * xMc / d };
+
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the <code>value</code> and <code>gradient</code> methods.
+ *
+ * @param parameters values of <tt>a</tt>, <tt>b</tt>, <tt>c</tt>, and
+ * <tt>d</tt>
+ *
+ * @throws IllegalArgumentException if <code>parameters</code> is
+ * <code>null</code> or if <code>parameters</code> does not have
+ * length == 4
+ * @throws ZeroException if <code>parameters[3]</code>
+ * (<tt>d</tt>) is 0
+ */
+ private void validateParameters(double[] parameters) throws ZeroException {
+ if (parameters == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (parameters.length != 4) {
+ throw new DimensionMismatchException(4, parameters.length);
+ }
+ if (parameters[3] == 0.0) {
+ throw new ZeroException();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/ParametricRealFunction.java b/src/main/java/org/apache/commons/math/optimization/fitting/ParametricRealFunction.java
new file mode 100644
index 0000000..0c2843e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/ParametricRealFunction.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.math.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * An interface representing a real function that depends on one independent
+ * variable plus some extra parameters.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public interface ParametricRealFunction {
+
+ /**
+ * Compute the value of the function.
+ * @param x the point for which the function value should be computed
+ * @param parameters function parameters
+ * @return the value
+ * @throws FunctionEvaluationException if the function evaluation fails
+ */
+ double value(double x, double[] parameters)
+ throws FunctionEvaluationException;
+
+ /**
+ * Compute the gradient of the function with respect to its parameters.
+ * @param x the point for which the function value should be computed
+ * @param parameters function parameters
+ * @return the value
+ * @throws FunctionEvaluationException if the function evaluation fails
+ */
+ double[] gradient(double x, double[] parameters)
+ throws FunctionEvaluationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/PolynomialFitter.java b/src/main/java/org/apache/commons/math/optimization/fitting/PolynomialFitter.java
new file mode 100644
index 0000000..3e8e62a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/PolynomialFitter.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.math.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+
+/** This class implements a curve fitting specialized for polynomials.
+ * <p>Polynomial fitting is a very simple case of curve fitting. The
+ * estimated coefficients are the polynomial coefficients. They are
+ * searched by a least square estimator.</p>
+ * @version $Revision: 1073270 $ $Date: 2011-02-22 10:19:27 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+ */
+
+public class PolynomialFitter {
+
+ /** Fitter for the coefficients. */
+ private final CurveFitter fitter;
+
+ /** Polynomial degree. */
+ 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
+ */
+ public PolynomialFitter(int degree, final DifferentiableMultivariateVectorialOptimizer optimizer) {
+ this.fitter = new CurveFitter(optimizer);
+ this.degree = degree;
+ }
+
+ /** 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 P(x) as close as possible to this value
+ */
+ public void addObservedPoint(double weight, double x, double y) {
+ fitter.addObservedPoint(weight, x, y);
+ }
+
+ /**
+ * Remove all observations.
+ * @since 2.2
+ */
+ public void clearObservations() {
+ fitter.clearObservations();
+ }
+
+ /** Get the polynomial fitting the weighted (x, y) points.
+ * @return polynomial function best fitting the observed points
+ * @exception OptimizationException if the algorithm failed to converge
+ */
+ public PolynomialFunction fit() throws OptimizationException {
+ try {
+ return new PolynomialFunction(fitter.fit(new ParametricPolynomial(), new double[degree + 1]));
+ } catch (FunctionEvaluationException fee) {
+ // should never happen
+ throw new RuntimeException(fee);
+ }
+ }
+
+ /** Dedicated parametric polynomial class. */
+ private static class ParametricPolynomial implements ParametricRealFunction {
+
+ /** {@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) {
+ double y = 0;
+ for (int i = parameters.length - 1; i >= 0; --i) {
+ y = y * x + parameters[i];
+ }
+ return y;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/fitting/WeightedObservedPoint.java b/src/main/java/org/apache/commons/math/optimization/fitting/WeightedObservedPoint.java
new file mode 100644
index 0000000..8153190
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/WeightedObservedPoint.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.math.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>
+ * @version $Revision: 786479 $ $Date: 2009-06-19 14:36:16 +0200 (ven. 19 juin 2009) $
+ * @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;
+ }
+
+ /** 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/math/optimization/fitting/package.html b/src/main/java/org/apache/commons/math/optimization/fitting/package.html
new file mode 100644
index 0000000..9f9fa06
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/fitting/package.html
@@ -0,0 +1,30 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision$ -->
+<body>
+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>
+
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/general/AbstractLeastSquaresOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/AbstractLeastSquaresOptimizer.java
new file mode 100644
index 0000000..6271f53
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/AbstractLeastSquaresOptimizer.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.general;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction;
+import org.apache.commons.math.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.InvalidMatrixException;
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.SimpleVectorialValueChecker;
+import org.apache.commons.math.optimization.VectorialConvergenceChecker;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Base class for implementing least squares optimizers.
+ * <p>This base class handles the boilerplate methods associated to thresholds
+ * settings, jacobian and error estimation.</p>
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 1.2
+ *
+ */
+public abstract class AbstractLeastSquaresOptimizer implements DifferentiableMultivariateVectorialOptimizer {
+
+ /** Default maximal number of iterations allowed. */
+ public static final int DEFAULT_MAX_ITERATIONS = 100;
+
+ /** Convergence checker. */
+ protected VectorialConvergenceChecker checker;
+
+ /**
+ * Jacobian matrix.
+ * <p>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).</p>
+ */
+ protected double[][] jacobian;
+
+ /** Number of columns of the jacobian matrix. */
+ protected int cols;
+
+ /** Number of rows of the jacobian matrix. */
+ protected int rows;
+
+ /**
+ * Target value for the objective functions at optimum.
+ * @since 2.1
+ */
+ protected double[] targetValues;
+
+ /**
+ * Weight for the least squares cost computation.
+ * @since 2.1
+ */
+ protected double[] residualsWeights;
+
+ /** Current point. */
+ protected double[] point;
+
+ /** Current objective function value. */
+ protected double[] objective;
+
+ /** Current residuals. */
+ protected double[] residuals;
+
+ /** Weighted Jacobian */
+ protected double[][] wjacobian;
+
+ /** Weighted residuals */
+ protected double[] wresiduals;
+
+ /** Cost value (square root of the sum of the residuals). */
+ protected double cost;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int objectiveEvaluations;
+
+ /** Number of jacobian evaluations. */
+ private int jacobianEvaluations;
+
+ /** Objective function. */
+ private DifferentiableMultivariateVectorialFunction function;
+
+ /** Objective function derivatives. */
+ private MultivariateMatrixFunction jF;
+
+ /** Simple constructor with default settings.
+ * <p>The convergence check is set to a {@link SimpleVectorialValueChecker}
+ * and the maximal number of evaluation is set to its default value.</p>
+ */
+ protected AbstractLeastSquaresOptimizer() {
+ setConvergenceChecker(new SimpleVectorialValueChecker());
+ setMaxIterations(DEFAULT_MAX_ITERATIONS);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return iterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return objectiveEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getJacobianEvaluations() {
+ return jacobianEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(VectorialConvergenceChecker convergenceChecker) {
+ this.checker = convergenceChecker;
+ }
+
+ /** {@inheritDoc} */
+ public VectorialConvergenceChecker getConvergenceChecker() {
+ return checker;
+ }
+
+ /** Increment the iterations counter by 1.
+ * @exception OptimizationException if the maximal number
+ * of iterations is exceeded
+ */
+ protected void incrementIterationsCounter()
+ throws OptimizationException {
+ if (++iterations > maxIterations) {
+ throw new OptimizationException(new MaxIterationsExceededException(maxIterations));
+ }
+ }
+
+ /**
+ * Update the jacobian matrix.
+ * @exception FunctionEvaluationException if the function jacobian
+ * cannot be evaluated or its dimension doesn't match problem dimension
+ */
+ protected void updateJacobian() throws FunctionEvaluationException {
+ ++jacobianEvaluations;
+ jacobian = jF.value(point);
+ if (jacobian.length != rows) {
+ throw new FunctionEvaluationException(point, LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ jacobian.length, rows);
+ }
+ for (int i = 0; i < rows; i++) {
+ final double[] ji = jacobian[i];
+ double wi = FastMath.sqrt(residualsWeights[i]);
+ for (int j = 0; j < cols; ++j) {
+ ji[j] *= -1.0;
+ wjacobian[i][j] = ji[j]*wi;
+ }
+ }
+ }
+
+ /**
+ * Update the residuals array and cost function value.
+ * @exception FunctionEvaluationException if the function cannot be evaluated
+ * or its dimension doesn't match problem dimension or maximal number of
+ * of evaluations is exceeded
+ */
+ protected void updateResidualsAndCost()
+ throws FunctionEvaluationException {
+
+ if (++objectiveEvaluations > maxEvaluations) {
+ throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations),
+ point);
+ }
+ objective = function.value(point);
+ if (objective.length != rows) {
+ throw new FunctionEvaluationException(point, LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ objective.length, rows);
+ }
+ cost = 0;
+ int index = 0;
+ for (int i = 0; i < rows; i++) {
+ final double residual = targetValues[i] - objective[i];
+ residuals[i] = residual;
+ wresiduals[i]= residual*FastMath.sqrt(residualsWeights[i]);
+ cost += residualsWeights[i] * residual * residual;
+ index += cols;
+ }
+ cost = FastMath.sqrt(cost);
+
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Get the covariance matrix of optimized parameters.
+ * @return covariance matrix
+ * @exception FunctionEvaluationException if the function jacobian cannot
+ * be evaluated
+ * @exception OptimizationException if the covariance matrix
+ * cannot be computed (singular problem)
+ */
+ public double[][] getCovariances()
+ throws FunctionEvaluationException, OptimizationException {
+
+ // set up the jacobian
+ updateJacobian();
+
+ // compute transpose(J).J, avoiding building big intermediate matrices
+ double[][] jTj = new double[cols][cols];
+ for (int i = 0; i < cols; ++i) {
+ for (int j = i; j < cols; ++j) {
+ double sum = 0;
+ for (int k = 0; k < rows; ++k) {
+ sum += wjacobian[k][i] * wjacobian[k][j];
+ }
+ jTj[i][j] = sum;
+ jTj[j][i] = sum;
+ }
+ }
+
+ try {
+ // compute the covariance matrix
+ RealMatrix inverse =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(jTj)).getSolver().getInverse();
+ return inverse.getData();
+ } catch (InvalidMatrixException ime) {
+ throw new OptimizationException(LocalizedFormats.UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM);
+ }
+
+ }
+
+ /**
+ * Guess the errors in optimized parameters.
+ * <p>Guessing is covariance-based, it only gives rough order of magnitude.</p>
+ * @return errors in optimized parameters
+ * @exception FunctionEvaluationException if the function jacobian cannot b evaluated
+ * @exception OptimizationException if the covariances matrix cannot be computed
+ * or the number of degrees of freedom is not positive (number of measurements
+ * lesser or equal to number of parameters)
+ */
+ public double[] guessParametersErrors()
+ throws FunctionEvaluationException, OptimizationException {
+ if (rows <= cols) {
+ throw new OptimizationException(
+ LocalizedFormats.NO_DEGREES_OF_FREEDOM,
+ rows, cols);
+ }
+ double[] errors = new double[cols];
+ final double c = FastMath.sqrt(getChiSquare() / (rows - cols));
+ double[][] covar = getCovariances();
+ for (int i = 0; i < errors.length; ++i) {
+ errors[i] = FastMath.sqrt(covar[i][i]) * c;
+ }
+ return errors;
+ }
+
+ /** {@inheritDoc} */
+ public VectorialPointValuePair optimize(final DifferentiableMultivariateVectorialFunction f,
+ final double[] target, final double[] weights,
+ final double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ if (target.length != weights.length) {
+ throw new OptimizationException(LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ target.length, weights.length);
+ }
+
+ // reset counters
+ iterations = 0;
+ objectiveEvaluations = 0;
+ jacobianEvaluations = 0;
+
+ // store least squares problem characteristics
+ function = f;
+ jF = f.jacobian();
+ targetValues = target.clone();
+ residualsWeights = weights.clone();
+ this.point = startPoint.clone();
+ this.residuals = new double[target.length];
+
+ // arrays shared with the other private methods
+ rows = target.length;
+ cols = point.length;
+ jacobian = new double[rows][cols];
+
+ wjacobian = new double[rows][cols];
+ wresiduals = new double[rows];
+
+ cost = Double.POSITIVE_INFINITY;
+
+ return doOptimize();
+
+ }
+
+ /** Perform the bulk of optimization algorithm.
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception FunctionEvaluationException if the objective function throws one during
+ * the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ protected abstract VectorialPointValuePair doOptimize()
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/AbstractScalarDifferentiableOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/AbstractScalarDifferentiableOptimizer.java
new file mode 100644
index 0000000..70f0a23
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/AbstractScalarDifferentiableOptimizer.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.math.optimization.general;
+
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateVectorialFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.optimization.DifferentiableMultivariateRealOptimizer;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealConvergenceChecker;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar functions.
+ * <p>This base class handles the boilerplate methods associated to thresholds
+ * settings, iterations and evaluations counting.</p>
+ * @version $Revision: 1069567 $ $Date: 2011-02-10 22:07:26 +0100 (jeu. 10 févr. 2011) $
+ * @since 2.0
+ */
+public abstract class AbstractScalarDifferentiableOptimizer
+ implements DifferentiableMultivariateRealOptimizer {
+
+ /** Default maximal number of iterations allowed. */
+ public static final int DEFAULT_MAX_ITERATIONS = 100;
+
+ /** Convergence checker. */
+ @Deprecated
+ protected RealConvergenceChecker checker;
+
+ /**
+ * Type of optimization.
+ * @since 2.1
+ */
+ @Deprecated
+ protected GoalType goal;
+
+ /** Current point set. */
+ @Deprecated
+ protected double[] point;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed. */
+ private int evaluations;
+
+ /** Number of gradient evaluations. */
+ private int gradientEvaluations;
+
+ /** Objective function. */
+ private DifferentiableMultivariateRealFunction function;
+
+ /** Objective function gradient. */
+ private MultivariateVectorialFunction gradient;
+
+ /** Simple constructor with default settings.
+ * <p>The convergence check is set to a {@link SimpleScalarValueChecker}
+ * and the maximal number of evaluation is set to its default value.</p>
+ */
+ protected AbstractScalarDifferentiableOptimizer() {
+ setConvergenceChecker(new SimpleScalarValueChecker());
+ setMaxIterations(DEFAULT_MAX_ITERATIONS);
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return iterations;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getGradientEvaluations() {
+ return gradientEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public void setConvergenceChecker(RealConvergenceChecker convergenceChecker) {
+ this.checker = convergenceChecker;
+ }
+
+ /** {@inheritDoc} */
+ public RealConvergenceChecker getConvergenceChecker() {
+ return checker;
+ }
+
+ /** Increment the iterations counter by 1.
+ * @exception OptimizationException if the maximal number
+ * of iterations is exceeded
+ */
+ protected void incrementIterationsCounter()
+ throws OptimizationException {
+ if (++iterations > maxIterations) {
+ throw new OptimizationException(new MaxIterationsExceededException(maxIterations));
+ }
+ }
+
+ /**
+ * Compute the gradient vector.
+ * @param evaluationPoint point at which the gradient must be evaluated
+ * @return gradient at the specified point
+ * @exception FunctionEvaluationException if the function gradient
+ */
+ protected double[] computeObjectiveGradient(final double[] evaluationPoint)
+ throws FunctionEvaluationException {
+ ++gradientEvaluations;
+ return gradient.value(evaluationPoint);
+ }
+
+ /**
+ * Compute the objective function value.
+ * @param evaluationPoint point at which the objective function must be evaluated
+ * @return objective function value at specified point
+ * @exception FunctionEvaluationException if the function cannot be evaluated
+ * or its dimension doesn't match problem dimension or the maximal number
+ * of iterations is exceeded
+ */
+ protected double computeObjectiveValue(final double[] evaluationPoint)
+ throws FunctionEvaluationException {
+ if (++evaluations > maxEvaluations) {
+ throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations),
+ evaluationPoint);
+ }
+ return function.value(evaluationPoint);
+ }
+
+ /** {@inheritDoc} */
+ public RealPointValuePair optimize(final DifferentiableMultivariateRealFunction f,
+ final GoalType goalType,
+ final double[] startPoint)
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ // reset counters
+ iterations = 0;
+ evaluations = 0;
+ gradientEvaluations = 0;
+
+ // store optimization problem characteristics
+ function = f;
+ gradient = f.gradient();
+ goal = goalType;
+ point = startPoint.clone();
+
+ return doOptimize();
+
+ }
+
+ /** Perform the bulk of optimization algorithm.
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception FunctionEvaluationException if the objective function throws one during
+ * the search
+ * @exception OptimizationException if the algorithm failed to converge
+ * @exception IllegalArgumentException if the start point dimension is wrong
+ */
+ protected abstract RealPointValuePair doOptimize()
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/ConjugateGradientFormula.java b/src/main/java/org/apache/commons/math/optimization/general/ConjugateGradientFormula.java
new file mode 100644
index 0000000..8a0fde3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/ConjugateGradientFormula.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.math.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
+ * @version $Revision: 758059 $ $Date: 2009-03-24 23:16:21 +0100 (mar. 24 mars 2009) $
+ * @since 2.0
+ */
+public enum ConjugateGradientFormula {
+
+ /** Fletcher-Reeves formula. */
+ FLETCHER_REEVES,
+
+ /** Polak-Ribi&egrave;re formula. */
+ POLAK_RIBIERE
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizer.java
new file mode 100644
index 0000000..e7ba606
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizer.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.math.optimization.general;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.DecompositionSolver;
+import org.apache.commons.math.linear.InvalidMatrixException;
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.QRDecompositionImpl;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @since 2.0
+ *
+ */
+
+public class GaussNewtonOptimizer extends AbstractLeastSquaresOptimizer {
+
+ /** Indicator for using LU decomposition. */
+ private final boolean useLU;
+
+ /** Simple constructor with default settings.
+ * <p>The convergence check is set to a {@link
+ * org.apache.commons.math.optimization.SimpleVectorialValueChecker}
+ * and the maximal number of evaluation is set to
+ * {@link AbstractLeastSquaresOptimizer#DEFAULT_MAX_ITERATIONS}.
+ * @param useLU if true, the normal equations will be solved using LU
+ * decomposition, otherwise they will be solved using QR decomposition
+ */
+ public GaussNewtonOptimizer(final boolean useLU) {
+ this.useLU = useLU;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public VectorialPointValuePair doOptimize()
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ // iterate until convergence is reached
+ VectorialPointValuePair current = null;
+ for (boolean converged = false; !converged;) {
+
+ incrementIterationsCounter();
+
+ // evaluate the objective function and its jacobian
+ VectorialPointValuePair previous = current;
+ updateResidualsAndCost();
+ updateJacobian();
+ current = new VectorialPointValuePair(point, objective);
+
+ // build the linear problem
+ final double[] b = new double[cols];
+ final double[][] a = new double[cols][cols];
+ for (int i = 0; i < rows; ++i) {
+
+ final double[] grad = jacobian[i];
+ final double weight = residualsWeights[i];
+ final double residual = objective[i] - targetValues[i];
+
+ // compute the normal equation
+ final double wr = weight * residual;
+ for (int j = 0; j < cols; ++j) {
+ b[j] += wr * grad[j];
+ }
+
+ // build the contribution matrix for measurement i
+ for (int k = 0; k < cols; ++k) {
+ double[] ak = a[k];
+ double wgk = weight * grad[k];
+ for (int l = 0; l < cols; ++l) {
+ ak[l] += wgk * grad[l];
+ }
+ }
+
+ }
+
+ try {
+
+ // solve the linearized least squares problem
+ RealMatrix mA = new BlockRealMatrix(a);
+ DecompositionSolver solver = useLU ?
+ new LUDecompositionImpl(mA).getSolver() :
+ new QRDecompositionImpl(mA).getSolver();
+ final double[] dX = solver.solve(b);
+
+ // update the estimated parameters
+ for (int i = 0; i < cols; ++i) {
+ point[i] += dX[i];
+ }
+
+ } catch(InvalidMatrixException e) {
+ throw new OptimizationException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM);
+ }
+
+ // check convergence
+ if (previous != null) {
+ converged = checker.converged(getIterations(), previous, current);
+ }
+
+ }
+
+ // we have converged
+ return current;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java
new file mode 100644
index 0000000..ea9bc03
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java
@@ -0,0 +1,888 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.optimization.general;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+
+/**
+ * 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>
+ * @version $Revision: 1073272 $ $Date: 2011-02-22 10:22:25 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+ *
+ */
+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 double initialStepBoundFactor;
+
+ /** Desired relative error in the sum of squares. */
+ private double costRelativeTolerance;
+
+ /** Desired relative error in the approximate solution parameters. */
+ private double parRelativeTolerance;
+
+ /** Desired max cosine on the orthogonality between the function vector
+ * and the columns of the jacobian. */
+ private double orthoTolerance;
+
+ /** Threshold for QR ranking. */
+ private double qrRankingThreshold;
+
+ /**
+ * Build an optimizer for least squares problems.
+ * <p>The default values for the algorithm settings are:
+ * <ul>
+ * <li>{@link #setConvergenceChecker(VectorialConvergenceChecker) vectorial convergence checker}: null</li>
+ * <li>{@link #setInitialStepBoundFactor(double) initial step bound factor}: 100.0</li>
+ * <li>{@link #setMaxIterations(int) maximal iterations}: 1000</li>
+ * <li>{@link #setCostRelativeTolerance(double) cost relative tolerance}: 1.0e-10</li>
+ * <li>{@link #setParRelativeTolerance(double) parameters relative tolerance}: 1.0e-10</li>
+ * <li>{@link #setOrthoTolerance(double) orthogonality tolerance}: 1.0e-10</li>
+ * <li>{@link #setQRRankingThreshold(double) QR ranking threshold}: {@link MathUtils#SAFE_MIN}</li>
+ * </ul>
+ * </p>
+ * <p>These default values may be overridden after construction. If the {@link
+ * #setConvergenceChecker vectorial convergence checker} is set to a non-null value, it
+ * will be used instead of the {@link #setCostRelativeTolerance cost relative tolerance}
+ * and {@link #setParRelativeTolerance parameters relative tolerance} settings.
+ */
+ public LevenbergMarquardtOptimizer() {
+
+ // set up the superclass with a default max cost evaluations setting
+ setMaxIterations(1000);
+
+ // default values for the tuning parameters
+ setConvergenceChecker(null);
+ setInitialStepBoundFactor(100.0);
+ setCostRelativeTolerance(1.0e-10);
+ setParRelativeTolerance(1.0e-10);
+ setOrthoTolerance(1.0e-10);
+ setQRRankingThreshold(MathUtils.SAFE_MIN);
+
+ }
+
+ /**
+ * Set the positive input variable used in determining the initial step bound.
+ * This bound is set to the product of initialStepBoundFactor and the euclidean
+ * norm of diag*x if nonzero, or else to initialStepBoundFactor itself. In most
+ * cases factor should lie in the interval (0.1, 100.0). 100.0 is a generally
+ * recommended value.
+ *
+ * @param initialStepBoundFactor initial step bound factor
+ */
+ public void setInitialStepBoundFactor(double initialStepBoundFactor) {
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ }
+
+ /**
+ * Set the desired relative error in the sum of squares.
+ * <p>This setting is used only if the {@link #setConvergenceChecker vectorial
+ * convergence checker} is set to null.</p>
+ * @param costRelativeTolerance desired relative error in the sum of squares
+ */
+ public void setCostRelativeTolerance(double costRelativeTolerance) {
+ this.costRelativeTolerance = costRelativeTolerance;
+ }
+
+ /**
+ * Set the desired relative error in the approximate solution parameters.
+ * <p>This setting is used only if the {@link #setConvergenceChecker vectorial
+ * convergence checker} is set to null.</p>
+ * @param parRelativeTolerance desired relative error
+ * in the approximate solution parameters
+ */
+ public void setParRelativeTolerance(double parRelativeTolerance) {
+ this.parRelativeTolerance = parRelativeTolerance;
+ }
+
+ /**
+ * Set the desired max cosine on the orthogonality.
+ * <p>This setting is always used, regardless of the {@link #setConvergenceChecker
+ * vectorial convergence checker} being null or non-null.</p>
+ * @param orthoTolerance desired max cosine on the orthogonality
+ * between the function vector and the columns of the jacobian
+ */
+ public void setOrthoTolerance(double orthoTolerance) {
+ this.orthoTolerance = orthoTolerance;
+ }
+
+ /**
+ * Set the desired threshold for QR ranking.
+ * <p>
+ * 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.
+ * </p>
+ * @param threshold threshold for QR ranking
+ * @since 2.2
+ */
+ public void setQRRankingThreshold(final double threshold) {
+ this.qrRankingThreshold = threshold;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected VectorialPointValuePair doOptimize()
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+
+ // arrays shared with the other private methods
+ solvedCols = Math.min(rows, cols);
+ diagR = new double[cols];
+ jacNorm = new double[cols];
+ beta = new double[cols];
+ permutation = new int[cols];
+ lmDir = new double[cols];
+
+ // local point
+ double delta = 0;
+ double xNorm = 0;
+ double[] diag = new double[cols];
+ double[] oldX = new double[cols];
+ double[] oldRes = new double[rows];
+ double[] oldObj = new double[rows];
+ double[] qtf = new double[rows];
+ double[] work1 = new double[cols];
+ double[] work2 = new double[cols];
+ double[] work3 = new double[cols];
+
+ // evaluate the function at the starting point and calculate its norm
+ updateResidualsAndCost();
+
+ // outer loop
+ lmPar = 0;
+ boolean firstIteration = true;
+ VectorialPointValuePair current = new VectorialPointValuePair(point, objective);
+ while (true) {
+ for (int i=0;i<rows;i++) {
+ qtf[i]=wresiduals[i];
+ }
+ incrementIterationsCounter();
+
+ // compute the Q.R. decomposition of the jacobian matrix
+ VectorialPointValuePair previous = current;
+ updateJacobian();
+ qrDecomposition();
+
+ // 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];
+ wjacobian[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 < cols; ++k) {
+ double dk = jacNorm[k];
+ if (dk == 0) {
+ dk = 1.0;
+ }
+ double xk = dk * point[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 (cost != 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 += wjacobian[i][pj] * qtf[i];
+ }
+ maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * cost));
+ }
+ }
+ }
+ if (maxCosine <= orthoTolerance) {
+ // convergence has been reached
+ updateResidualsAndCost();
+ current = new VectorialPointValuePair(point, objective);
+ return current;
+ }
+
+ // rescale if necessary
+ for (int j = 0; j < cols; ++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] = point[pj];
+ }
+ double previousCost = cost;
+ double[] tmpVec = residuals;
+ residuals = oldRes;
+ oldRes = tmpVec;
+ tmpVec = objective;
+ objective = 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];
+ point[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
+ updateResidualsAndCost();
+
+ // compute the scaled actual reduction
+ double actRed = -1.0;
+ if (0.1 * cost < previousCost) {
+ double r = cost / 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] += wjacobian[i][pj] * dirJ;
+ }
+ }
+ double coeff1 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ coeff1 += work1[j] * work1[j];
+ }
+ double pc2 = previousCost * previousCost;
+ coeff1 = 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 * cost >= 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 < cols; ++k) {
+ double xK = diag[k] * point[k];
+ xNorm += xK * xK;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+ current = new VectorialPointValuePair(point, objective);
+
+ // tests for convergence.
+ if (checker != null) {
+ // we use the vectorial convergence checker
+ if (checker.converged(getIterations(), previous, current)) {
+ return current;
+ }
+ }
+ } else {
+ // failed iteration, reset the previous values
+ cost = previousCost;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ point[pj] = oldX[pj];
+ }
+ tmpVec = residuals;
+ residuals = oldRes;
+ oldRes = tmpVec;
+ tmpVec = objective;
+ objective = oldObj;
+ oldObj = tmpVec;
+ }
+ if (checker==null) {
+ if (((FastMath.abs(actRed) <= costRelativeTolerance) &&
+ (preRed <= costRelativeTolerance) &&
+ (ratio <= 2.0)) ||
+ (delta <= parRelativeTolerance * xNorm)) {
+ 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 OptimizationException(LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE,
+ costRelativeTolerance);
+ } else if (delta <= 2.2204e-16 * xNorm) {
+ throw new OptimizationException(LocalizedFormats.TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE,
+ parRelativeTolerance);
+ } else if (maxCosine <= 2.2204e-16) {
+ throw new OptimizationException(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) {
+
+ // 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 < cols; ++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 * wjacobian[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 += wjacobian[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 += wjacobian[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]] -= wjacobian[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) {
+ wjacobian[i][pj] = wjacobian[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 = wjacobian[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)
+ wjacobian[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 = wjacobian[i][pk];
+ final double temp2 = cos * rik + sin * lmDiag[i];
+ lmDiag[i] = -sin * rik + cos * lmDiag[i];
+ wjacobian[i][pk] = temp2;
+ }
+
+ }
+ }
+
+ // store the diagonal element of s and restore
+ // the corresponding diagonal element of R
+ lmDiag[j] = wjacobian[j][permutation[j]];
+ wjacobian[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 += wjacobian[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>
+ * @exception OptimizationException if the decomposition cannot be performed
+ */
+ private void qrDecomposition() throws OptimizationException {
+
+ // initializations
+ for (int k = 0; k < cols; ++k) {
+ permutation[k] = k;
+ double norm2 = 0;
+ for (int i = 0; i < wjacobian.length; ++i) {
+ double akk = wjacobian[i][k];
+ norm2 += akk * akk;
+ }
+ jacNorm[k] = FastMath.sqrt(norm2);
+ }
+
+ // transform the matrix column after column
+ for (int k = 0; k < cols; ++k) {
+
+ // select the column with the greatest norm on active components
+ int nextColumn = -1;
+ double ak2 = Double.NEGATIVE_INFINITY;
+ for (int i = k; i < cols; ++i) {
+ double norm2 = 0;
+ for (int j = k; j < wjacobian.length; ++j) {
+ double aki = wjacobian[j][permutation[i]];
+ norm2 += aki * aki;
+ }
+ if (Double.isInfinite(norm2) || Double.isNaN(norm2)) {
+ throw new OptimizationException(LocalizedFormats.UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN,
+ rows, cols);
+ }
+ 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 = wjacobian[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;
+ wjacobian[k][pk] -= alpha;
+
+ // transform the remaining columns
+ for (int dk = cols - 1 - k; dk > 0; --dk) {
+ double gamma = 0;
+ for (int j = k; j < wjacobian.length; ++j) {
+ gamma += wjacobian[j][pk] * wjacobian[j][permutation[k + dk]];
+ }
+ gamma *= betak;
+ for (int j = k; j < wjacobian.length; ++j) {
+ wjacobian[j][permutation[k + dk]] -= gamma * wjacobian[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) {
+ for (int k = 0; k < cols; ++k) {
+ int pk = permutation[k];
+ double gamma = 0;
+ for (int i = k; i < rows; ++i) {
+ gamma += wjacobian[i][pk] * y[i];
+ }
+ gamma *= beta[pk];
+ for (int i = k; i < rows; ++i) {
+ y[i] -= gamma * wjacobian[i][pk];
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizer.java
new file mode 100644
index 0000000..17f1d8b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizer.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.math.optimization.general;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.solvers.BrentSolver;
+import org.apache.commons.math.analysis.solvers.UnivariateRealSolver;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ *
+ */
+
+public class NonLinearConjugateGradientOptimizer
+ extends AbstractScalarDifferentiableOptimizer {
+
+ /** Update formula for the beta parameter. */
+ private final ConjugateGradientFormula updateFormula;
+
+ /** Preconditioner (may be null). */
+ private Preconditioner preconditioner;
+
+ /** solver to use in the line search (may be null). */
+ private UnivariateRealSolver solver;
+
+ /** Initial step used to bracket the optimum in line search. */
+ private double initialStep;
+
+ /** Simple constructor with default settings.
+ * <p>The convergence check is set to a {@link
+ * org.apache.commons.math.optimization.SimpleVectorialValueChecker}
+ * and the maximal number of iterations is set to
+ * {@link AbstractScalarDifferentiableOptimizer#DEFAULT_MAX_ITERATIONS}.
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link ConjugateGradientFormula#FLETCHER_REEVES} or {@link
+ * ConjugateGradientFormula#POLAK_RIBIERE}
+ */
+ public NonLinearConjugateGradientOptimizer(final ConjugateGradientFormula updateFormula) {
+ this.updateFormula = updateFormula;
+ preconditioner = null;
+ solver = null;
+ initialStep = 1.0;
+ }
+
+ /**
+ * Set the preconditioner.
+ * @param preconditioner preconditioner to use for next optimization,
+ * may be null to remove an already registered preconditioner
+ */
+ public void setPreconditioner(final Preconditioner preconditioner) {
+ this.preconditioner = preconditioner;
+ }
+
+ /**
+ * Set the solver to use during line search.
+ * @param lineSearchSolver solver to use during line search, may be null
+ * to remove an already registered solver and fall back to the
+ * default {@link BrentSolver Brent solver}.
+ */
+ public void setLineSearchSolver(final UnivariateRealSolver lineSearchSolver) {
+ this.solver = lineSearchSolver;
+ }
+
+ /**
+ * 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 RealPointValuePair doOptimize()
+ throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
+ try {
+
+ // initialization
+ if (preconditioner == null) {
+ preconditioner = new IdentityPreconditioner();
+ }
+ if (solver == null) {
+ solver = new BrentSolver();
+ }
+ 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];
+ }
+
+ RealPointValuePair current = null;
+ while (true) {
+
+ final double objective = computeObjectiveValue(point);
+ RealPointValuePair previous = current;
+ current = new RealPointValuePair(point, objective);
+ if (previous != null) {
+ if (checker.converged(getIterations(), previous, current)) {
+ // we have found an optimum
+ return current;
+ }
+ }
+
+ incrementIterationsCounter();
+
+ double dTd = 0;
+ for (final double di : searchDirection) {
+ dTd += di * di;
+ }
+
+ // find the optimal step in the search direction
+ final UnivariateRealFunction lsf = new LineSearchFunction(searchDirection);
+ final double step = solver.solve(lsf, 0, findUpperBound(lsf, 0, initialStep));
+
+ // 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 ((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];
+ }
+ }
+
+ }
+
+ } catch (ConvergenceException ce) {
+ throw new OptimizationException(ce);
+ }
+ }
+
+ /**
+ * 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
+ * @exception FunctionEvaluationException if the function cannot be computed
+ * @exception OptimizationException if no bracket can be found
+ */
+ private double findUpperBound(final UnivariateRealFunction f,
+ final double a, final double h)
+ throws FunctionEvaluationException, OptimizationException {
+ 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 OptimizationException(LocalizedFormats.UNABLE_TO_BRACKET_OPTIMUM_IN_LINE_SEARCH);
+ }
+
+ /** Default identity preconditioner. */
+ private 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 UnivariateRealFunction {
+ /** Search direction. */
+ private final double[] searchDirection;
+
+ /** Simple constructor.
+ * @param searchDirection search direction
+ */
+ public LineSearchFunction(final double[] searchDirection) {
+ this.searchDirection = searchDirection;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) throws FunctionEvaluationException {
+
+ // 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;
+ 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/math/optimization/general/Preconditioner.java b/src/main/java/org/apache/commons/math/optimization/general/Preconditioner.java
new file mode 100644
index 0000000..7bdde75
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/Preconditioner.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.math.optimization.general;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * This interface represents a preconditioner for differentiable scalar
+ * objective function optimizers.
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ * @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
+ * @exception FunctionEvaluationException if no cost can be computed for the parameters
+ * @exception IllegalArgumentException if point dimension is wrong
+ */
+ double[] precondition(double[] point, double[] r)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/general/package.html b/src/main/java/org/apache/commons/math/optimization/general/package.html
new file mode 100644
index 0000000..51ccf95
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/general/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 799857 $ -->
+<body>
+This package provides optimization algorithms that require derivatives.
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/linear/AbstractLinearOptimizer.java b/src/main/java/org/apache/commons/math/optimization/linear/AbstractLinearOptimizer.java
new file mode 100644
index 0000000..b8b2390
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/AbstractLinearOptimizer.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.math.optimization.linear;
+
+import java.util.Collection;
+
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+
+/**
+ * Base class for implementing linear optimizers.
+ * <p>This base class handles the boilerplate methods associated to thresholds
+ * settings and iterations counters.</p>
+ * @version $Revision: 925812 $ $Date: 2010-03-21 16:49:31 +0100 (dim. 21 mars 2010) $
+ * @since 2.0
+ *
+ */
+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
+ */
+ protected LinearObjectiveFunction function;
+
+ /**
+ * Linear constraints.
+ * @since 2.1
+ */
+ protected Collection<LinearConstraint> linearConstraints;
+
+ /**
+ * Type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @since 2.1
+ */
+ protected GoalType goal;
+
+ /**
+ * Whether to restrict the variables to non-negative values.
+ * @since 2.1
+ */
+ protected 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);
+ }
+
+ /** {@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 OptimizationException if the maximal number
+ * of iterations is exceeded
+ */
+ protected void incrementIterationsCounter()
+ throws OptimizationException {
+ if (++iterations > maxIterations) {
+ throw new OptimizationException(new MaxIterationsExceededException(maxIterations));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealPointValuePair optimize(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType, final boolean restrictToNonNegative)
+ throws OptimizationException {
+
+ // 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 OptimizationException if no solution fulfilling the constraints
+ * can be found in the allowed number of iterations
+ */
+ protected abstract RealPointValuePair doOptimize()
+ throws OptimizationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/linear/LinearConstraint.java b/src/main/java/org/apache/commons/math/optimization/linear/LinearConstraint.java
new file mode 100644
index 0000000..85d6251
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/LinearConstraint.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.math.optimization.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.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>
+ * @version $Revision: 922713 $ $Date: 2010-03-14 02:26:13 +0100 (dim. 14 mars 2010) $
+ * @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;
+ }
+
+ /**
+ * 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/math/optimization/linear/LinearObjectiveFunction.java b/src/main/java/org/apache/commons/math/optimization/linear/LinearObjectiveFunction.java
new file mode 100644
index 0000000..b3c3eb8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/LinearObjectiveFunction.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.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>
+ * @version $Revision: 922713 $ $Date: 2010-03-14 02:26:13 +0100 (dim. 14 mars 2010) $
+ * @since 2.0
+ */
+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(point) + 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/math/optimization/linear/LinearOptimizer.java b/src/main/java/org/apache/commons/math/optimization/linear/LinearOptimizer.java
new file mode 100644
index 0000000..41fccd9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/LinearOptimizer.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.math.optimization.linear;
+
+import java.util.Collection;
+
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+
+/**
+ * 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>
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+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 OptimizationException if no solution fulfilling the constraints
+ * can be found in the allowed number of iterations
+ */
+ RealPointValuePair optimize(LinearObjectiveFunction f, Collection<LinearConstraint> constraints,
+ GoalType goalType, boolean restrictToNonNegative)
+ throws OptimizationException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/linear/NoFeasibleSolutionException.java b/src/main/java/org/apache/commons/math/optimization/linear/NoFeasibleSolutionException.java
new file mode 100644
index 0000000..b145dd4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/NoFeasibleSolutionException.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.math.optimization.linear;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.OptimizationException;
+
+/**
+ * This class represents exceptions thrown by optimizers when no solution
+ * fulfills the constraints.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class NoFeasibleSolutionException extends OptimizationException {
+
+ /** 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/math/optimization/linear/Relationship.java b/src/main/java/org/apache/commons/math/optimization/linear/Relationship.java
new file mode 100644
index 0000000..500dcc1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/Relationship.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.math.optimization.linear;
+
+/**
+ * Types of relationships between two cells in a Solver {@link LinearConstraint}.
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ * @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
+ */
+ private 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/math/optimization/linear/SimplexSolver.java b/src/main/java/org/apache/commons/math/optimization/linear/SimplexSolver.java
new file mode 100644
index 0000000..830f65c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/SimplexSolver.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.math.optimization.linear;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.util.MathUtils;
+
+
+/**
+ * Solves a linear problem using the Two-Phase Simplex Method.
+ * @version $Revision: 812831 $ $Date: 2009-09-09 10:48:03 +0200 (mer. 09 sept. 2009) $
+ * @since 2.0
+ */
+public class SimplexSolver extends AbstractLinearOptimizer {
+
+ /** Default amount of error to accept in floating point comparisons. */
+ private static final double DEFAULT_EPSILON = 1.0e-6;
+
+ /** Amount of error to accept in floating point comparisons. */
+ protected final double epsilon;
+
+ /**
+ * Build a simplex solver with default settings.
+ */
+ public SimplexSolver() {
+ this(DEFAULT_EPSILON);
+ }
+
+ /**
+ * Build a simplex solver with a specified accepted amount of error
+ * @param epsilon the amount of error to accept in floating point comparisons
+ */
+ public SimplexSolver(final double epsilon) {
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * 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++) {
+ if (MathUtils.compareTo(tableau.getEntry(0, i), minValue, epsilon) < 0) {
+ minValue = tableau.getEntry(0, i);
+ 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 (MathUtils.compareTo(entry, 0, epsilon) > 0) {
+ final double ratio = rhs / entry;
+ if (MathUtils.equals(ratio, minRatio, epsilon)) {
+ minRatioPositions.add(i);
+ } else if (ratio < minRatio) {
+ 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
+ // check if there's an artificial variable that can be forced out of the basis
+ for (Integer row : minRatioPositions) {
+ for (int i = 0; i < tableau.getNumArtificialVariables(); i++) {
+ int column = i + tableau.getArtificialVariableOffset();
+ if (MathUtils.equals(tableau.getEntry(row, column), 1, epsilon) &&
+ row.equals(tableau.getBasicRow(column))) {
+ return row;
+ }
+ }
+ }
+ }
+ return minRatioPositions.get(0);
+ }
+
+ /**
+ * Runs one iteration of the Simplex method on the given model.
+ * @param tableau simple tableau for the problem
+ * @throws OptimizationException if the maximal iteration count has been
+ * exceeded or if the model is found not to have a bounded solution
+ */
+ protected void doIteration(final SimplexTableau tableau)
+ throws OptimizationException {
+
+ 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) {
+ 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
+ * @exception OptimizationException if the maximal number of iterations is
+ * exceeded, or if the problem is found not to have a bounded solution, or
+ * if there is no feasible solution
+ */
+ protected void solvePhase1(final SimplexTableau tableau) throws OptimizationException {
+
+ // 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 (!MathUtils.equals(tableau.getEntry(0, tableau.getRhsOffset()), 0, epsilon)) {
+ throw new NoFeasibleSolutionException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealPointValuePair doOptimize() throws OptimizationException {
+ final SimplexTableau tableau =
+ new SimplexTableau(function, linearConstraints, goal, nonNegative, epsilon);
+
+ solvePhase1(tableau);
+ tableau.dropPhase1Objective();
+
+ while (!tableau.isOptimal()) {
+ doIteration(tableau);
+ }
+ return tableau.getSolution();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java b/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java
new file mode 100644
index 0000000..c25619a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java
@@ -0,0 +1,589 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.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 org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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>
+ * @version $Revision: 922713 $ $Date: 2010-03-14 02:26:13 +0100 (dim. 14 mars 2010) $
+ * @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 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 in floating point comparisons. */
+ private final double epsilon;
+
+ /**
+ * 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 in floating point comparisons
+ */
+ SimplexTableau(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType, final boolean restrictToNonNegative,
+ final double epsilon) {
+ this.f = f;
+ this.constraints = normalizeConstraints(constraints);
+ this.restrictToNonNegative = restrictToNonNegative;
+ this.epsilon = epsilon;
+ 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.getData(), matrix.getDataRef()[zIndex]);
+ matrix.setEntry(zIndex, width - 1,
+ maximize ? f.getConstantTerm() : -1 * f.getConstantTerm());
+
+ if (!restrictToNonNegative) {
+ matrix.setEntry(zIndex, getSlackVariableOffset() - 1,
+ getInvertedCoeffiecientSum(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().getData(), matrix.getDataRef()[row]);
+
+ // x-
+ if (!restrictToNonNegative) {
+ matrix.setEntry(row, getSlackVariableOffset() - 1,
+ getInvertedCoeffiecientSum(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>();
+ 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 getInvertedCoeffiecientSum(final RealVector coefficients) {
+ double sum = 0;
+ for (double coefficient : coefficients.getData()) {
+ 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++) {
+ if (MathUtils.equals(getEntry(i, col), 1.0, epsilon) && (row == null)) {
+ row = i;
+ } else if (!MathUtils.equals(getEntry(i, col), 0.0, epsilon)) {
+ 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;
+ }
+
+ List<Integer> columnsToDrop = new ArrayList<Integer>();
+ columnsToDrop.add(0);
+
+ // positive cost non-artificial variables
+ for (int i = getNumObjectiveFunctions(); i < getArtificialVariableOffset(); i++) {
+ if (MathUtils.compareTo(tableau.getEntry(0, i), 0, 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);
+ }
+ }
+ }
+
+ for (int i = columnsToDrop.size() - 1; i >= 0; i--) {
+ columnLabels.remove((int) columnsToDrop.get(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++) {
+ if (MathUtils.compareTo(tableau.getEntry(0, i), 0, epsilon) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the current solution.
+ *
+ * @return current solution
+ */
+ protected RealPointValuePair 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 (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;
+ } else {
+ basicRows.add(basicRow);
+ coefficients[i] =
+ (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
+ (restrictToNonNegative ? 0 : mostNegative);
+ }
+ }
+ return new RealPointValuePair(coefficients, f.getValue(coefficients));
+ }
+
+ /**
+ * Subtracts a multiple of one row from another.
+ * <p>
+ * After application of this operation, the following will hold:
+ * minuendRow = minuendRow - multiple * subtrahendRow
+ * </p>
+ * @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:
+ * minuendRow = minuendRow - multiple * subtrahendRow
+ * </p>
+ * @param minuendRow row index
+ * @param subtrahendRow row index
+ * @param multiple multiplication factor
+ */
+ protected void subtractRow(final int minuendRow, final int subtrahendRow,
+ final double multiple) {
+ tableau.setRowVector(minuendRow, tableau.getRowVector(minuendRow)
+ .subtract(tableau.getRowVector(subtrahendRow).mapMultiply(multiple)));
+ }
+
+ /**
+ * 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.
+ * </p>
+ * @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) &&
+ 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() ^
+ 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/math/optimization/linear/UnboundedSolutionException.java b/src/main/java/org/apache/commons/math/optimization/linear/UnboundedSolutionException.java
new file mode 100644
index 0000000..b769c32
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/UnboundedSolutionException.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.math.optimization.linear;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.OptimizationException;
+
+/**
+ * This class represents exceptions thrown by optimizers when a solution
+ * escapes to infinity.
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @since 2.0
+ */
+public class UnboundedSolutionException extends OptimizationException {
+
+ /** 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/math/optimization/linear/package.html b/src/main/java/org/apache/commons/math/optimization/linear/package.html
new file mode 100644
index 0000000..beb79a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/linear/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 758920 $ -->
+<body>
+This package provides optimization algorithms for linear constrained problems.
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/package.html b/src/main/java/org/apache/commons/math/optimization/package.html
new file mode 100644
index 0000000..8550a80
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/package.html
@@ -0,0 +1,72 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 758074 $ -->
+<body>
+<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>
+
+<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.math.optimization.UnivariateRealOptimizer
+ UnivariateRealOptimizer} for {@link org.apache.commons.math.analysis.UnivariateRealFunction
+ univariate real functions}</li>
+ <li>{@link org.apache.commons.math.optimization.MultivariateRealOptimizer
+ MultivariateRealOptimizer} for {@link org.apache.commons.math.analysis.MultivariateRealFunction
+ multivariate real functions}</li>
+ <li>{@link org.apache.commons.math.optimization.DifferentiableMultivariateRealOptimizer
+ DifferentiableMultivariateRealOptimizer} for {@link
+ org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction
+ differentiable multivariate real functions}</li>
+ <li>{@link org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer
+ DifferentiableMultivariateVectorialOptimizer} for {@link
+ org.apache.commons.math.analysis.DifferentiableMultivariateVectorialFunction
+ differentiable multivariate vectorial functions}</li>
+</ul>
+</p>
+
+<p>
+Despite there are only four types of supported optimizers, it is possible to optimize a
+transform a {@link org.apache.commons.math.analysis.MultivariateVectorialFunction
+non-differentiable multivariate vectorial function} by converting it to a {@link
+org.apache.commons.math.analysis.MultivariateRealFunction non-differentiable multivariate
+real function} thanks to the {@link
+org.apache.commons.math.optimization.LeastSquaresConverter LeastSquaresConverter} helper class.
+The transformed function can be optimized using any implementation of the {@link
+org.apache.commons.math.optimization.MultivariateRealOptimizer MultivariateRealOptimizer} interface.
+</p>
+
+<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.
+</p>
+</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java b/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.java
new file mode 100644
index 0000000..399a1b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/univariate/AbstractUnivariateRealOptimizer.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.math.optimization.univariate;
+
+import org.apache.commons.math.ConvergingAlgorithmImpl;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.MathUnsupportedOperationException;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.UnivariateRealOptimizer;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * optimizers.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public abstract class AbstractUnivariateRealOptimizer
+ extends ConvergingAlgorithmImpl implements UnivariateRealOptimizer {
+ /** Indicates where a root has been computed. */
+ protected boolean resultComputed;
+ /** The last computed root. */
+ protected double result;
+ /** Value of the function at the last computed result. */
+ protected double functionValue;
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+ /** Number of evaluations already performed. */
+ private int evaluations;
+ /** Optimization type */
+ private GoalType optimizationGoal;
+ /** 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 UnivariateRealFunction function;
+
+ /**
+ * Construct a solver with given iteration count and accuracy.
+ * @param defaultAbsoluteAccuracy maximum absolute error
+ * @param defaultMaximalIterationCount maximum number of iterations
+ * @throws IllegalArgumentException if f is null or the
+ * defaultAbsoluteAccuracy is not valid
+ * @deprecated in 2.2. Please use the "setter" methods to assign meaningful
+ * values to the maximum numbers of iterations and evaluations, and to the
+ * absolute and relative accuracy thresholds.
+ */
+ @Deprecated
+ protected AbstractUnivariateRealOptimizer(final int defaultMaximalIterationCount,
+ final double defaultAbsoluteAccuracy) {
+ super(defaultMaximalIterationCount, defaultAbsoluteAccuracy);
+ resultComputed = false;
+ setMaxEvaluations(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Default constructor.
+ * To be removed once the single non-default one has been removed.
+ */
+ protected AbstractUnivariateRealOptimizer() {}
+
+ /**
+ * Check whether a result has been computed.
+ * @throws NoDataException if no result has been computed
+ * @deprecated in 2.2 (no alternative).
+ */
+ @Deprecated
+ protected void checkResultComputed() {
+ if (!resultComputed) {
+ throw new NoDataException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double getResult() {
+ if (!resultComputed) {
+ throw new NoDataException();
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public double getFunctionValue() throws FunctionEvaluationException {
+ if (Double.isNaN(functionValue)) {
+ final double opt = getResult();
+ functionValue = function.value(opt);
+ }
+ return functionValue;
+ }
+
+ /**
+ * Convenience function for implementations.
+ *
+ * @param x the result to set
+ * @param fx the result to set
+ * @param iterationCount the iteration count to set
+ * @deprecated in 2.2 (no alternative).
+ */
+ @Deprecated
+ protected final void setResult(final double x, final double fx,
+ final int iterationCount) {
+ this.result = x;
+ this.functionValue = fx;
+ this.iterationCount = iterationCount;
+ this.resultComputed = true;
+ }
+
+ /**
+ * Convenience function for implementations.
+ * @deprecated in 2.2 (no alternative).
+ */
+ @Deprecated
+ protected final void clearResult() {
+ this.resultComputed = false;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ this.maxEvaluations = maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ public GoalType getGoalType() {
+ return optimizationGoal;
+ }
+ /**
+ * @return the lower of the search interval.
+ */
+ public double getMin() {
+ return searchMin;
+ }
+ /**
+ * @return the higher of the search interval.
+ */
+ public double getMax() {
+ return searchMax;
+ }
+ /**
+ * @return the initial guess.
+ */
+ public double getStartValue() {
+ return searchStart;
+ }
+
+ /**
+ * Compute the objective function value.
+ * @param f objective function
+ * @param point point at which the objective function must be evaluated
+ * @return objective function value at specified point
+ * @exception FunctionEvaluationException if the function cannot be evaluated
+ * or the maximal number of iterations is exceeded
+ * @deprecated in 2.2. Use this {@link #computeObjectiveValue(double)
+ * replacement} instead.
+ */
+ @Deprecated
+ protected double computeObjectiveValue(final UnivariateRealFunction f,
+ final double point)
+ throws FunctionEvaluationException {
+ if (++evaluations > maxEvaluations) {
+ throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations), point);
+ }
+ return f.value(point);
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at specified point.
+ * @exception FunctionEvaluationException if the function cannot be evaluated
+ * or the maximal number of iterations is exceeded.
+ */
+ protected double computeObjectiveValue(double point)
+ throws FunctionEvaluationException {
+ if (++evaluations > maxEvaluations) {
+ resultComputed = false;
+ throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations), point);
+ }
+ return function.value(point);
+ }
+
+ /** {@inheritDoc} */
+ public double optimize(UnivariateRealFunction f, GoalType goal,
+ double min, double max, double startValue)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ // Initialize.
+ this.searchMin = min;
+ this.searchMax = max;
+ this.searchStart = startValue;
+ this.optimizationGoal = goal;
+ this.function = f;
+
+ // Reset.
+ functionValue = Double.NaN;
+ evaluations = 0;
+ resetIterationsCounter();
+
+ // Perform computation.
+ result = doOptimize();
+ resultComputed = true;
+
+ return result;
+ }
+
+ /**
+ * Set the value at the optimum.
+ *
+ * @param functionValue Value of the objective function at the optimum.
+ */
+ protected void setFunctionValue(double functionValue) {
+ this.functionValue = functionValue;
+ }
+
+ /** {@inheritDoc} */
+ public double optimize(UnivariateRealFunction f, GoalType goal,
+ double min, double max)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return optimize(f, goal, min, max, min + 0.5 * (max - min));
+ }
+
+ /**
+ * Method for implementing actual optimization algorithms in derived
+ * classes.
+ *
+ * From version 3.0 onwards, this method will be abstract - i.e.
+ * concrete implementations will have to implement it. If this method
+ * is not implemented, subclasses must override
+ * {@link #optimize(UnivariateRealFunction, GoalType, double, double)}.
+ *
+ * @return the optimum.
+ * @throws MaxIterationsExceededException if the maximum iteration count
+ * is exceeded.
+ * @throws FunctionEvaluationException if an error occurs evaluating
+ * the function.
+ */
+ protected double doOptimize()
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ throw new MathUnsupportedOperationException(LocalizedFormats.NOT_OVERRIDEN);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/univariate/BracketFinder.java b/src/main/java/org/apache/commons/math/optimization/univariate/BracketFinder.java
new file mode 100644
index 0000000..18e7015
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/univariate/BracketFinder.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.math.optimization.univariate;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.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).
+ * @version $Revision$ $Date$
+ * @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;
+ /**
+ * Maximum number of iterations.
+ */
+ private final int maxIterations;
+ /**
+ * Number of iterations.
+ */
+ private int iterations;
+ /**
+ * Number of function evaluations.
+ */
+ private int 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, 50} (see the
+ * {@link #BracketFinder(double,int) other constructor}).
+ */
+ public BracketFinder() {
+ this(100, 50);
+ }
+
+ /**
+ * Create a bracketing interval finder.
+ *
+ * @param growLimit Expanding factor.
+ * @param maxIterations Maximum number of iterations allowed for finding
+ * a bracketing interval.
+ */
+ public BracketFinder(double growLimit,
+ int maxIterations) {
+ if (growLimit <= 0) {
+ throw new NotStrictlyPositiveException(growLimit);
+ }
+ if (maxIterations <= 0) {
+ throw new NotStrictlyPositiveException(maxIterations);
+ }
+
+ this.growLimit = growLimit;
+ this.maxIterations = maxIterations;
+ }
+
+ /**
+ * Search new points that bracket a local optimum of the function.
+ *
+ * @param func Function whose optimum should be bracketted.
+ * @param goal {@link GoalType Goal type}.
+ * @param xA Initial point.
+ * @param xB Initial point.
+ * @throws MaxIterationsExceededException if the maximum iteration count
+ * is exceeded.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function.
+ */
+ public void search(UnivariateRealFunction func,
+ GoalType goal,
+ double xA,
+ double xB)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ reset();
+ 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) {
+ if (++iterations > maxIterations) {
+ throw new MaxIterationsExceededException(maxIterations);
+ }
+
+ double tmp1 = (xB - xA) * (fB - fC);
+ double tmp2 = (xB - xC) * (fB - fA);
+
+ double val = tmp2 - tmp1;
+ double denom = Math.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;
+ xB = xC;
+ xC = w;
+ fA = fB;
+ fB = fC;
+ fC = fW;
+ }
+
+ lo = xA;
+ mid = xB;
+ hi = xC;
+ fLo = fA;
+ fMid = fB;
+ fHi = fC;
+ }
+
+ /**
+ * @return the number of iterations.
+ */
+ public int getIterations() {
+ return iterations;
+ }
+ /**
+ * @return the number of evaluations.
+ */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /**
+ * @return the lower bound of the bracket.
+ * @see #getFLow()
+ */
+ public double getLo() {
+ return lo;
+ }
+
+ /**
+ * Get function value at {@link #getLo()}.
+ * @return function value at {@link #getLo()}
+ */
+ public double getFLow() {
+ 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 FunctionEvaluationException if function cannot be evaluated at x
+ */
+ private double eval(UnivariateRealFunction f, double x)
+ throws FunctionEvaluationException {
+
+ ++evaluations;
+ return f.value(x);
+ }
+
+ /**
+ * Reset internal state.
+ */
+ private void reset() {
+ iterations = 0;
+ evaluations = 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java b/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.java
new file mode 100644
index 0000000..73d1d8c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/univariate/BrentOptimizer.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.math.optimization.univariate;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements Richard Brent's algorithm (from his book "Algorithms for
+ * Minimization without Derivatives", p. 79) for finding minima of real
+ * univariate functions. This implementation is an adaptation partly
+ * based on the Python code from SciPy (module "optimize.py" v0.5).
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
+ /**
+ * Golden section.
+ */
+ private static final double GOLDEN_SECTION = 0.5 * (3 - FastMath.sqrt(5));
+
+ /**
+ * Construct a solver.
+ */
+ public BrentOptimizer() {
+ setMaxEvaluations(1000);
+ setMaximalIterationCount(100);
+ setAbsoluteAccuracy(1e-11);
+ setRelativeAccuracy(1e-9);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doOptimize()
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ return localMin(getGoalType() == GoalType.MINIMIZE,
+ getMin(), getStartValue(), getMax(),
+ getRelativeAccuracy(), getAbsoluteAccuracy());
+ }
+
+ /**
+ * Find the minimum of the function within the interval {@code (lo, hi)}.
+ *
+ * If the function is defined on the interval {@code (lo, hi)}, then
+ * this method finds an approximation {@code x} to the point at which
+ * the function attains its minimum.<br/>
+ * {@code t} and {@code eps} define a tolerance {@code tol = eps |x| + t}
+ * and the function is never evaluated at two points closer together than
+ * {@code tol}. {@code eps} should be no smaller than <em>2 macheps</em> and
+ * preferable not much less than <em>sqrt(macheps)</em>, where
+ * <em>macheps</em> is the relative machine precision. {@code t} should be
+ * positive.
+ * @param isMinim {@code true} when minimizing the function.
+ * @param lo Lower bound of the interval.
+ * @param mid Point inside the interval {@code [lo, hi]}.
+ * @param hi Higher bound of the interval.
+ * @param eps Relative accuracy.
+ * @param t Absolute accuracy.
+ * @return the optimum point.
+ * @throws MaxIterationsExceededException if the maximum iteration count
+ * is exceeded.
+ * @throws FunctionEvaluationException if an error occurs evaluating the function.
+ */
+ private double localMin(boolean isMinim,
+ double lo, double mid, double hi,
+ double eps, double t)
+ throws MaxIterationsExceededException, FunctionEvaluationException {
+ if (eps <= 0) {
+ throw new NotStrictlyPositiveException(eps);
+ }
+ if (t <= 0) {
+ throw new NotStrictlyPositiveException(t);
+ }
+ 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;
+
+ while (true) {
+ double m = 0.5 * (a + b);
+ final double tol1 = eps * FastMath.abs(x) + t;
+ final double tol2 = 2 * tol1;
+
+ // Check stopping criterion.
+ if (FastMath.abs(x - m) > tol2 - 0.5 * (b - a)) {
+ 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;
+ }
+
+ // 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 || w == x) {
+ v = w;
+ fv = fw;
+ w = u;
+ fw = fu;
+ } else if (fu <= fv || v == x || v == w) {
+ v = u;
+ fv = fu;
+ }
+ }
+ } else { // termination
+ setFunctionValue(isMinim ? fx : -fx);
+ return x;
+ }
+ incrementIterationsCounter();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/optimization/univariate/package.html b/src/main/java/org/apache/commons/math/optimization/univariate/package.html
new file mode 100644
index 0000000..d792fc0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/optimization/univariate/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Univariate real functions minimum finding algorithms.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/package.html b/src/main/java/org/apache/commons/math/package.html
new file mode 100644
index 0000000..bdf939c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Common classes used throughout the commons-math library.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/random/AbstractRandomGenerator.java b/src/main/java/org/apache/commons/math/random/AbstractRandomGenerator.java
new file mode 100644
index 0000000..afec63e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/AbstractRandomGenerator.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.math.random;
+
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.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.</p>
+ *
+ * @since 1.1
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+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}. Implemementations 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 underyling 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.</p>
+ *
+ * @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}.</p>
+ *
+ * @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 = 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></p>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code int}
+ * value from this random number generator's sequence
+ */
+ public int nextInt() {
+ return (int) (nextDouble() * 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></p>
+ *
+ * @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></p>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code long}
+ *value from this random number generator's sequence
+ */
+ public long nextLong() {
+ return (long) (nextDouble() * 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></p>
+ *
+ * @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></p>
+ *
+ * @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.</p>
+ *
+ * @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>
+ * <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)}.</p>
+ *
+ * @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/math/random/AbstractWell.java b/src/main/java/org/apache/commons/math/random/AbstractWell.java
new file mode 100644
index 0000000..96e18a2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/AbstractWell.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.math.random;
+
+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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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 as the
+ * seed.</p>
+ * @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, System.currentTimeMillis());
+ }
+
+ /** 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.</p>
+ * @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.</p>
+ * @param seed the initial seed (32 bits integers array), if null
+ * the seed of the generator will be related to the current time
+ */
+ @Override
+ public void setSeed(final int[] seed) {
+
+ if (seed == null) {
+ setSeed(System.currentTimeMillis());
+ return;
+ }
+
+ System.arraycopy(seed, 0, v, 0, Math.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;
+
+ }
+
+ /** 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.</p>
+ * @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/math/random/BitsStreamGenerator.java b/src/main/java/org/apache/commons/math/random/BitsStreamGenerator.java
new file mode 100644
index 0000000..8979473
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/BitsStreamGenerator.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.math.random;
+
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.util.FastMath;
+
+/** Base class for random number generators that generates bits streams.
+
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @since 2.0
+
+ */
+public abstract class BitsStreamGenerator implements RandomGenerator {
+
+ /** 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()}.</p>
+ * @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 void nextBytes(byte[] bytes) {
+ int i = 0;
+ final int iEnd = bytes.length - 3;
+ while (i < iEnd) {
+ final int random = next(32);
+ bytes[i] = (byte) (random & 0xff);
+ bytes[i + 1] = (byte) ((random >> 8) & 0xff);
+ bytes[i + 2] = (byte) ((random >> 16) & 0xff);
+ bytes[i + 3] = (byte) ((random >> 24) & 0xff);
+ i += 4;
+ }
+ int random = next(32);
+ while (i < bytes.length) {
+ bytes[i++] = (byte) (random & 0xff);
+ random = random >> 8;
+ }
+ }
+
+ /** {@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} */
+ public int nextInt(int n) throws IllegalArgumentException {
+
+ if (n < 1) {
+ throw new NotStrictlyPositiveException(n);
+ }
+
+ // find bit mask for n
+ int mask = n;
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+
+ while (true) {
+ final int random = next(32) & mask;
+ if (random < n) {
+ return random;
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public long nextLong() {
+ final long high = ((long) next(32)) << 32;
+ final long low = ((long) next(32)) & 0xffffffffL;
+ return high | low;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java b/src/main/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java
new file mode 100644
index 0000000..f1df51d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.java
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.random;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.NotPositiveDefiniteMatrixException;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ * <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>
+ * <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.</p>
+ *
+ * @version $Revision: 1043908 $ $Date: 2010-12-09 12:53:14 +0100 (jeu. 09 déc. 2010) $
+ * @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;
+
+ /** Permutated Cholesky root of the covariance matrix. */
+ private RealMatrix root;
+
+ /** Rank of the covariance matrix. */
+ private int rank;
+
+ /** Simple constructor.
+ * <p>Build a correlated random vector generator from its mean
+ * vector and covariance matrix.</p>
+ * @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
+ * @exception IllegalArgumentException if there is a dimension
+ * mismatch between the mean vector and the covariance matrix
+ * @exception NotPositiveDefiniteMatrixException if the
+ * covariance matrix is not strictly positive definite
+ * @exception DimensionMismatchException if the mean and covariance
+ * arrays dimensions don't match
+ */
+ public CorrelatedRandomVectorGenerator(double[] mean,
+ RealMatrix covariance, double small,
+ NormalizedRandomGenerator generator)
+ throws NotPositiveDefiniteMatrixException, DimensionMismatchException {
+
+ int order = covariance.getRowDimension();
+ if (mean.length != order) {
+ throw new DimensionMismatchException(mean.length, order);
+ }
+ this.mean = mean.clone();
+
+ decompose(covariance, small);
+
+ this.generator = generator;
+ normalized = new double[rank];
+
+ }
+
+ /** Simple constructor.
+ * <p>Build a null mean random correlated vector generator from its
+ * covariance matrix.</p>
+ * @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
+ * @exception NotPositiveDefiniteMatrixException if the
+ * covariance matrix is not strictly positive definite
+ */
+ public CorrelatedRandomVectorGenerator(RealMatrix covariance, double small,
+ NormalizedRandomGenerator generator)
+ throws NotPositiveDefiniteMatrixException {
+
+ int order = covariance.getRowDimension();
+ mean = new double[order];
+ for (int i = 0; i < order; ++i) {
+ mean[i] = 0;
+ }
+
+ decompose(covariance, small);
+
+ this.generator = generator;
+ normalized = new double[rank];
+
+ }
+
+ /** Get the underlying normalized components generator.
+ * @return underlying uncorrelated components generator
+ */
+ public NormalizedRandomGenerator getGenerator() {
+ return generator;
+ }
+
+ /** 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 covariance matrix.
+ * The rank is the number of independent rows in the covariance
+ * matrix, it is also the number of columns of the rectangular
+ * matrix of the decomposition.
+ * @return rank of the square matrix.
+ * @see #getRootMatrix()
+ */
+ public int getRank() {
+ return rank;
+ }
+
+ /** Decompose the original square matrix.
+ * <p>The decomposition is based on a Choleski decomposition
+ * where additional transforms are performed:
+ * <ul>
+ * <li>the rows of the decomposed matrix are permuted</li>
+ * <li>columns with the too small diagonal element are discarded</li>
+ * <li>the matrix is permuted</li>
+ * </ul>
+ * This means that rather than computing M = U<sup>T</sup>.U where U
+ * is an upper triangular matrix, this method computed M=B.B<sup>T</sup>
+ * where B is a rectangular matrix.
+ * @param covariance covariance matrix
+ * @param small diagonal elements threshold under which column are
+ * considered to be dependent on previous ones and are discarded
+ * @exception NotPositiveDefiniteMatrixException if the
+ * covariance matrix is not strictly positive definite
+ */
+ private void decompose(RealMatrix covariance, double small)
+ throws NotPositiveDefiniteMatrixException {
+
+ int order = covariance.getRowDimension();
+ double[][] c = covariance.getData();
+ double[][] b = new double[order][order];
+
+ int[] swap = new int[order];
+ int[] index = new int[order];
+ for (int i = 0; i < order; ++i) {
+ index[i] = i;
+ }
+
+ rank = 0;
+ for (boolean loop = true; loop;) {
+
+ // find maximal diagonal element
+ swap[rank] = rank;
+ for (int i = rank + 1; i < order; ++i) {
+ int ii = index[i];
+ int isi = index[swap[i]];
+ if (c[ii][ii] > c[isi][isi]) {
+ swap[rank] = i;
+ }
+ }
+
+
+ // swap elements
+ if (swap[rank] != rank) {
+ int tmp = index[rank];
+ index[rank] = index[swap[rank]];
+ index[swap[rank]] = tmp;
+ }
+
+ // check diagonal element
+ int ir = index[rank];
+ if (c[ir][ir] < small) {
+
+ if (rank == 0) {
+ throw new NotPositiveDefiniteMatrixException();
+ }
+
+ // check remaining diagonal elements
+ for (int i = rank; i < order; ++i) {
+ if (c[index[i]][index[i]] < -small) {
+ // there is at least one sufficiently negative diagonal element,
+ // the covariance matrix is wrong
+ throw new NotPositiveDefiniteMatrixException();
+ }
+ }
+
+ // all remaining diagonal elements are close to zero,
+ // we consider we have found the rank of the covariance matrix
+ ++rank;
+ loop = false;
+
+ } else {
+
+ // transform the matrix
+ double sqrt = FastMath.sqrt(c[ir][ir]);
+ b[rank][rank] = sqrt;
+ double inverse = 1 / sqrt;
+ for (int i = rank + 1; i < order; ++i) {
+ int ii = index[i];
+ double e = inverse * c[ii][ir];
+ b[i][rank] = e;
+ c[ii][ii] -= e * e;
+ for (int j = rank + 1; j < i; ++j) {
+ int ij = index[j];
+ double f = c[ii][ij] - e * b[j][rank];
+ c[ii][ij] = f;
+ c[ij][ii] = f;
+ }
+ }
+
+ // prepare next iteration
+ loop = ++rank < order;
+
+ }
+
+ }
+
+ // build the root matrix
+ root = MatrixUtils.createRealMatrix(order, rank);
+ for (int i = 0; i < order; ++i) {
+ for (int j = 0; j < rank; ++j) {
+ root.setEntry(index[i], j, b[i][j]);
+ }
+ }
+
+ }
+
+ /** 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 < rank; ++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 < rank; ++j) {
+ correlated[i] += root.getEntry(i, j) * normalized[j];
+ }
+ }
+
+ return correlated;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/EmpiricalDistribution.java b/src/main/java/org/apache/commons/math/random/EmpiricalDistribution.java
new file mode 100644
index 0000000..7f08a06
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/EmpiricalDistribution.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.random;
+
+import java.io.IOException;
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.commons.math.stat.descriptive.StatisticalSummary;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+
+/**
+ * Represents an <a href="http://random.mat.sbg.ac.at/~ste/dipl/node11.html">
+ * 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>
+ * Implementations of this interface maintain 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>
+ * <li>dividing the input data into "bin ranges" and reporting bin frequency
+ * counts (data for histogram)</li>
+ * <li>reporting univariate statistics describing the full set of data values
+ * as well as the observations within each bin</li>
+ * <li>generating random values from the distribution</li>
+ * </ul>
+ * Applications can use <code>EmpiricalDistribution</code> implementations 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>
+ *
+ * @version $Revision: 817128 $ $Date: 2009-09-21 03:30:53 +0200 (lun. 21 sept. 2009) $
+ */
+public interface EmpiricalDistribution {
+
+ /**
+ * Computes the empirical distribution from the provided
+ * array of numbers.
+ *
+ * @param dataArray the data array
+ */
+ void load(double[] dataArray);
+
+ /**
+ * Computes the empirical distribution from the input file.
+ *
+ * @param file the input file
+ * @throws IOException if an IO error occurs
+ */
+ void load(File file) throws IOException;
+
+ /**
+ * Computes the empirical distribution using data read from a URL.
+ *
+ * @param url url of the input file
+ * @throws IOException if an IO error occurs
+ */
+ void load(URL url) throws IOException;
+
+ /**
+ * Generates a random value from this distribution.
+ * <strong>Preconditions:</strong><ul>
+ * <li>the distribution must be loaded before invoking this method</li></ul>
+ * @return the random value.
+ *
+ * @throws IllegalStateException if the distribution has not been loaded
+ */
+ double getNextValue() throws IllegalStateException;
+
+
+ /**
+ * Returns a
+ * {@link org.apache.commons.math.stat.descriptive.StatisticalSummary}
+ * describing this distribution.
+ * <strong>Preconditions:</strong><ul>
+ * <li>the distribution must be loaded before invoking this method</li>
+ * </ul>
+ *
+ * @return the sample statistics
+ * @throws IllegalStateException if the distribution has not been loaded
+ */
+ StatisticalSummary getSampleStats() throws IllegalStateException;
+
+ /**
+ * Property indicating whether or not the distribution has been loaded.
+ *
+ * @return true if the distribution has been loaded
+ */
+ boolean isLoaded();
+
+ /**
+ * Returns the number of bins.
+ *
+ * @return the number of bins
+ */
+ int getBinCount();
+
+ /**
+ * Returns a list of
+ * {@link org.apache.commons.math.stat.descriptive.SummaryStatistics}
+ * containing statistics describing the values in each of the bins. The
+ * List is indexed on the bin number.
+ *
+ * @return List of bin statistics
+ */
+ List<SummaryStatistics> getBinStats();
+
+ /**
+ * Returns 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].
+ *
+ * @return array of bin upper bounds
+ */
+ double[] getUpperBounds();
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java b/src/main/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java
new file mode 100644
index 0000000..a3ae8a7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/EmpiricalDistributionImpl.java
@@ -0,0 +1,479 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.random;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.StatisticalSummary;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements <code>EmpiricalDistribution</code> interface. This 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>
+ * <li>Divide the range from min-max into <code>binCount</code> "bins."</li>
+ * <li>Pass the data file again, computing bin counts and univariate
+ * statistics (mean, std dev.) for each of the bins </li>
+ * <li>Divide the interval (0,1) into subintervals associated with the bins,
+ * with the length of a bin's subinterval proportional to its count.</li></ol>
+ * <strong>Generating random values from the distribution</strong><ol>
+ * <li>Generate a uniformly distributed value in (0,1) </li>
+ * <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.</li></ol></p><p>
+ *<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>
+ *<li>The input file <i>must</i> be a plain text file containing one valid numeric
+ * entry per line.</li>
+ * </ul></p>
+ *
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ */
+public class EmpiricalDistributionImpl implements Serializable, EmpiricalDistribution {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5729073523949762654L;
+
+ /** 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;
+
+ /** RandomData instance to use in repeated calls to getNext() */
+ private final RandomData randomData = new RandomDataImpl();
+
+ /**
+ * Creates a new EmpiricalDistribution with the default bin count.
+ */
+ public EmpiricalDistributionImpl() {
+ binCount = 1000;
+ binStats = new ArrayList<SummaryStatistics>();
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with the specified bin count.
+ *
+ * @param binCount number of bins
+ */
+ public EmpiricalDistributionImpl(int binCount) {
+ this.binCount = binCount;
+ binStats = new ArrayList<SummaryStatistics>();
+ }
+
+ /**
+ * Computes the empirical distribution from the provided
+ * array of numbers.
+ *
+ * @param in the input data array
+ */
+ public void load(double[] in) {
+ DataAdapter da = new ArrayDataAdapter(in);
+ try {
+ da.computeStats();
+ fillBinStats(in);
+ } catch (IOException e) {
+ throw new MathRuntimeException(e);
+ }
+ loaded = true;
+
+ }
+
+ /**
+ * Computes the empirical distribution using data read from a URL.
+ * @param url url of the input file
+ *
+ * @throws IOException if an IO error occurs
+ */
+ public void load(URL url) throws IOException {
+ BufferedReader in =
+ new BufferedReader(new InputStreamReader(url.openStream()));
+ try {
+ DataAdapter da = new StreamDataAdapter(in);
+ da.computeStats();
+ if (sampleStats.getN() == 0) {
+ throw MathRuntimeException.createEOFException(LocalizedFormats.URL_CONTAINS_NO_DATA,
+ url);
+ }
+ in = new BufferedReader(new InputStreamReader(url.openStream()));
+ fillBinStats(in);
+ loaded = true;
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Computes the empirical distribution from the input file.
+ *
+ * @param file the input file
+ * @throws IOException if an IO error occurs
+ */
+ public void load(File file) throws IOException {
+ BufferedReader in = new BufferedReader(new FileReader(file));
+ try {
+ DataAdapter da = new StreamDataAdapter(in);
+ da.computeStats();
+ in = new BufferedReader(new FileReader(file));
+ fillBinStats(in);
+ loaded = true;
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ex) {
+ // 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;
+
+ }
+
+ /**
+ * Factory of <code>DataAdapter</code> objects. For every supported source
+ * of data (array of doubles, file, etc.) an instance of the proper object
+ * is returned.
+ */
+ private class DataAdapterFactory{
+ /**
+ * Creates a DataAdapter from a data object
+ *
+ * @param in object providing access to the data
+ * @return DataAdapter instance
+ */
+ public DataAdapter getAdapter(Object in) {
+ if (in instanceof BufferedReader) {
+ BufferedReader inputStream = (BufferedReader) in;
+ return new StreamDataAdapter(inputStream);
+ } else if (in instanceof double[]) {
+ double[] inputArray = (double[]) in;
+ return new ArrayDataAdapter(inputArray);
+ } else {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INPUT_DATA_FROM_UNSUPPORTED_DATASOURCE,
+ in.getClass().getName(),
+ BufferedReader.class.getName(), double[].class.getName());
+ }
+ }
+ }
+ /**
+ * <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
+ */
+ public 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.valueOf(str).doubleValue();
+ 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
+ */
+ public ArrayDataAdapter(double[] in){
+ super();
+ 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 in object providing access to the data
+ * @throws IOException if an IO error occurs
+ */
+ private void fillBinStats(Object in) throws IOException {
+ // Set up grid
+ min = sampleStats.getMin();
+ max = sampleStats.getMax();
+ delta = (max - min)/(Double.valueOf(binCount)).doubleValue();
+
+ // 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
+ DataAdapterFactory aFactory = new DataAdapterFactory();
+ DataAdapter da = aFactory.getAdapter(in);
+ 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.
+ *
+ * @return the random value.
+ * @throws IllegalStateException if the distribution has not been loaded
+ */
+ public double getNextValue() throws IllegalStateException {
+
+ if (!loaded) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.DISTRIBUTION_NOT_LOADED);
+ }
+
+ // Start with a uniformly distributed random number in (0,1)
+ double x = FastMath.random();
+
+ // Use this to select the bin and generate a Gaussian within the bin
+ for (int i = 0; i < binCount; i++) {
+ if (x <= upperBounds[i]) {
+ SummaryStatistics stats = binStats.get(i);
+ if (stats.getN() > 0) {
+ if (stats.getStandardDeviation() > 0) { // more than one obs
+ return randomData.nextGaussian
+ (stats.getMean(),stats.getStandardDeviation());
+ } else {
+ return stats.getMean(); // only one obs in bin
+ }
+ }
+ }
+ }
+ throw new MathRuntimeException(LocalizedFormats.NO_BIN_SELECTED);
+ }
+
+ /**
+ * Returns a {@link StatisticalSummary} describing this distribution.
+ * <strong>Preconditions:</strong><ul>
+ * <li>the distribution must be loaded before invoking this method</li></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;
+ }
+
+ /**
+ * <p>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>
+ *
+ * <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()}.</p>
+ *
+ * @return array of bin upper bounds
+ * @since 2.1
+ */
+ public double[] getUpperBounds() {
+ double[] binUpperBounds = new double[binCount];
+ binUpperBounds[0] = min + delta;
+ for (int i = 1; i < binCount - 1; i++) {
+ binUpperBounds[i] = binUpperBounds[i-1] + delta;
+ }
+ binUpperBounds[binCount - 1] = max;
+ return binUpperBounds;
+ }
+
+ /**
+ * <p>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.</p>
+ *
+ * <p>In versions 1.0-2.0 of commons-math, this array was (incorrectly) returned
+ * by {@link #getUpperBounds()}.</p>
+ *
+ * @since 2.1
+ * @return array of upper bounds of subintervals used in data generation
+ */
+ 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;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/random/GaussianRandomGenerator.java b/src/main/java/org/apache/commons/math/random/GaussianRandomGenerator.java
new file mode 100644
index 0000000..6bd9775
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/GaussianRandomGenerator.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.math.random;
+
+/**
+ * This class is a gaussian normalized random generator for scalars.
+ * <p>This class is a simple wrapper around the {@link
+ * RandomGenerator#nextGaussian} method.</p>
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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/math/random/JDKRandomGenerator.java b/src/main/java/org/apache/commons/math/random/JDKRandomGenerator.java
new file mode 100644
index 0000000..573ef61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/JDKRandomGenerator.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.math.random;
+
+import java.util.Random;
+
+/**
+ * Extension of <code>java.util.Random</code> to implement
+ * {@link RandomGenerator}.
+ *
+ * @since 1.1
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class JDKRandomGenerator extends Random implements RandomGenerator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7745277476784028798L;
+
+ /** {@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);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/MersenneTwister.java b/src/main/java/org/apache/commons/math/random/MersenneTwister.java
new file mode 100644
index 0000000..6a6fadd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/MersenneTwister.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.math.random;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.util.FastMath;
+
+
+/** 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>
+
+ * <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>
+
+ * <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:</p>
+
+ * <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>
+
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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 as the
+ * seed.</p>
+ */
+ public MersenneTwister() {
+ mt = new int[N];
+ setSeed(System.currentTimeMillis());
+ }
+
+ /** 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.</p>
+ * @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;
+ 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;
+ }
+ }
+
+ /** 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.</p>
+ * @param seed the initial seed (32 bits integers array), if null
+ * the seed of the generator will be related to the current time
+ */
+ @Override
+ public void setSeed(int[] seed) {
+
+ if (seed == null) {
+ setSeed(System.currentTimeMillis());
+ 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
+
+ }
+
+ /** 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.</p>
+ * @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()}.</p>
+ * @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/math/random/NormalizedRandomGenerator.java b/src/main/java/org/apache/commons/math/random/NormalizedRandomGenerator.java
new file mode 100644
index 0000000..0f13b81
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.random;
+
+/**
+ * This interface represent a normalized random generator for
+ * scalars.
+ * Normalized generator provide null mean and unit standard deviation scalars.
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ * @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.</p>
+ * @return a random scalar with null mean and unit standard deviation
+ */
+ double nextNormalizedDouble();
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/RandomAdaptor.java b/src/main/java/org/apache/commons/math/random/RandomAdaptor.java
new file mode 100644
index 0000000..8091a48
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/RandomAdaptor.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.math.random;
+
+import java.util.Random;
+
+/**
+ * Extension of <code>java.util.Random</code> wrapping a
+ * {@link RandomGenerator}.
+ *
+ * @since 1.1
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ */
+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 <tt>int</tt> 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 <tt>int</tt> 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 <tt>int</tt>
+ * 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 <tt>long</tt> 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/math/random/RandomData.java b/src/main/java/org/apache/commons/math/random/RandomData.java
new file mode 100644
index 0000000..0fc5136
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/RandomData.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.math.random;
+import java.util.Collection;
+
+/**
+ * Random data generation utilities.
+ * @version $Revision: 780975 $ $Date: 2009-06-02 11:05:37 +0200 (mar. 02 juin 2009) $
+ */
+public interface RandomData {
+ /**
+ * Generates a random string of hex characters of length
+ * <code>len</code>.
+ * <p>
+ * The generated string will be random, but not cryptographically
+ * secure. To generate cryptographically secure strings, use
+ * <code>nextSecureHexString</code></p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>len > 0</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @param len the length of the string to be generated
+ * @return random string of hex characters of length <code>len</code>
+ */
+ String nextHexString(int len);
+
+ /**
+ * Generates a uniformly distributed random integer between
+ * <code>lower</code> and <code>upper</code> (endpoints included).
+ * <p>
+ * The generated integer will be random, but not cryptographically secure.
+ * To generate cryptographically secure integer sequences, use
+ * <code>nextSecureInt</code>.</p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>lower < upper</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @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</code>
+ * and less than or equal to <code>upper</code>.
+ */
+ int nextInt(int lower, int upper);
+
+ /**
+ * Generates a uniformly distributed random long integer between
+ * <code>lower</code> and <code>upper</code> (endpoints included).
+ * <p>
+ * The generated long integer values will be random, but not
+ * cryptographically secure.
+ * To generate cryptographically secure sequences of longs, use
+ * <code>nextSecureLong</code></p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>lower < upper</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @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</code>
+ * and less than or equal to <code>upper</code>.
+ */
+ long nextLong(long lower, long upper);
+
+ /**
+ * Generates a random string of hex characters from a secure random
+ * sequence.
+ * <p>
+ * If cryptographic security is not required,
+ * use <code>nextHexString()</code>.</p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>len > 0</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ * @param len length of return string
+ * @return the random hex string
+ */
+ String nextSecureHexString(int len);
+
+ /**
+ * Generates a uniformly distributed random integer between
+ * <code>lower</code> and <code>upper</code> (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,
+ * <code>nextInt</code> should be used instead of this method.</p>
+ * <p>
+ * <strong>Definition</strong>:
+ * <a href="http://en.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
+ * Secure Random Sequence</a></p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>lower < upper</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @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</code>
+ * and less than or equal to <code>upper</code>.
+ */
+ int nextSecureInt(int lower, int upper);
+
+ /**
+ * Generates a random long integer between <code>lower</code>
+ * and <code>upper</code> (endpoints included).
+ * <p>
+ * Sequences of long values generated using this method will be
+ * cryptographically secure. If cryptographic security is not required,
+ * <code>nextLong</code> should be used instead of this method.</p>
+ * <p>
+ * <strong>Definition</strong>:
+ * <a href="http://en.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
+ * Secure Random Sequence</a></p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>lower < upper</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @param lower lower bound for generated integer
+ * @param upper upper bound for generated integer
+ * @return a long integer greater than or equal to <code>lower</code>
+ * and less than or equal to <code>upper</code>.
+ */
+ long nextSecureLong(long lower, long upper);
+
+ /**
+ * 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></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The specified mean <i>must</i> be positive (otherwise an
+ * IllegalArgumentException is thrown.)</li>
+ * </ul></p>
+ * @param mean Mean of the distribution
+ * @return poisson deviate with the specified mean
+ */
+ long nextPoisson(double mean);
+
+ /**
+ * Generates a random value from the
+ * Normal (or Gaussian) distribution with the given mean
+ * and standard deviation.
+ * <p>
+ * <strong>Definition</strong>:
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm">
+ * Normal Distribution</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li><code>sigma > 0</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ * @param mu Mean of the distribution
+ * @param sigma Standard deviation of the distribution
+ * @return random value from Gaussian distribution with mean = mu,
+ * standard deviation = sigma
+ */
+ double nextGaussian(double mu, double sigma);
+
+ /**
+ * Generates a random value from the exponential distribution
+ * with expected value = <code>mean</code>.
+ * <p>
+ * <strong>Definition</strong>:
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm">
+ * Exponential Distribution</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li><code>mu >= 0</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ * @param mean Mean of the distribution
+ * @return random value from exponential distribution
+ */
+ double nextExponential(double mean);
+
+ /**
+ * Generates a uniformly distributed random value from the open interval
+ * (<code>lower</code>,<code>upper</code>) (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</code> and
+ * <code>upper - lower</code> are the
+ * <a href = "http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm">
+ * location and scale parameters</a>, respectively.</p>
+ * <p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>lower < upper</code> (otherwise an IllegalArgumentException
+ * is thrown.)</li>
+ * </ul></p>
+ *
+ * @param lower lower endpoint of the interval of support
+ * @param upper upper endpoint of the interval of support
+ * @return uniformly distributed random value between lower
+ * and upper (exclusive)
+ */
+ double nextUniform(double lower, double upper);
+
+ /**
+ * Generates an integer array of length <code>k</code> whose entries
+ * are selected randomly, without repetition, from the integers <code>
+ * 0 through n-1</code> (inclusive).
+ * <p>
+ * Generated arrays represent permutations
+ * of <code>n</code> taken <code>k</code> at a time.</p>
+ * <p>
+ * <strong>Preconditions:</strong><ul>
+ * <li> <code>k <= n</code></li>
+ * <li> <code>n > 0</code> </li>
+ * </ul>
+ * If the preconditions are not met, an IllegalArgumentException is
+ * thrown.</p>
+ *
+ * @param n domain of the permutation
+ * @param k size of the permutation
+ * @return random k-permutation of n
+ */
+ int[] nextPermutation(int n, int k);
+
+ /**
+ * Returns an array of <code>k</code> objects selected randomly
+ * from the Collection <code>c</code>.
+ * <p>
+ * Sampling from <code>c</code>
+ * is without replacement; but if <code>c</code> contains identical
+ * objects, the sample may include repeats. If all elements of <code>
+ * c</code> 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</code> from the elements of <code>c</code>.</p>
+ * <p>
+ * <strong>Preconditions:</strong><ul>
+ * <li> k must be less than or equal to the size of c </li>
+ * <li> c must not be empty </li>
+ * </ul>
+ * If the preconditions are not met, an IllegalArgumentException is
+ * thrown.</p>
+ *
+ * @param c collection to be sampled
+ * @param k size of the sample
+ * @return random sample of k elements from c
+ */
+ Object[] nextSample(Collection<?> c, int k);
+}
diff --git a/src/main/java/org/apache/commons/math/random/RandomDataImpl.java b/src/main/java/org/apache/commons/math/random/RandomDataImpl.java
new file mode 100644
index 0000000..e9ccab7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/RandomDataImpl.java
@@ -0,0 +1,966 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.random;
+
+import java.io.Serializable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.util.Collection;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.distribution.BetaDistributionImpl;
+import org.apache.commons.math.distribution.BinomialDistributionImpl;
+import org.apache.commons.math.distribution.CauchyDistributionImpl;
+import org.apache.commons.math.distribution.ChiSquaredDistributionImpl;
+import org.apache.commons.math.distribution.ContinuousDistribution;
+import org.apache.commons.math.distribution.FDistributionImpl;
+import org.apache.commons.math.distribution.GammaDistributionImpl;
+import org.apache.commons.math.distribution.HypergeometricDistributionImpl;
+import org.apache.commons.math.distribution.IntegerDistribution;
+import org.apache.commons.math.distribution.PascalDistributionImpl;
+import org.apache.commons.math.distribution.TDistributionImpl;
+import org.apache.commons.math.distribution.WeibullDistributionImpl;
+import org.apache.commons.math.distribution.ZipfDistributionImpl;
+import org.apache.commons.math.exception.MathInternalError;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.apache.commons.math.exception.NumberIsTooLargeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * 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 generator based on {@link java.util.Random}. 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>
+ * <p>
+ * For details on the default PRNGs, see {@link java.util.Random} and
+ * {@link java.security.SecureRandom}.
+ * </p>
+ * <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>
+ * <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>
+ * <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 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>
+ * <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>
+ * <li>
+ * This implementation is not synchronized.
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 1061496 $ $Date: 2011-01-20 21:32:16 +0100 (jeu. 20 janv. 2011) $
+ */
+public class RandomDataImpl 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 SecureRandom secRand = null;
+
+ /**
+ * Construct a RandomDataImpl.
+ */
+ public RandomDataImpl() {
+ }
+
+ /**
+ * 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
+ * @since 1.1
+ */
+ public RandomDataImpl(RandomGenerator rand) {
+ super();
+ this.rand = rand;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * <strong>Algorithm Description:</strong> hex strings are generated using a
+ * 2-step process.
+ * <ol>
+ * <li>
+ * len/2+1 binary bytes are generated using the underlying Random</li>
+ * <li>
+ * Each binary byte is translated into 2 hex digits</li>
+ * </ol>
+ * </p>
+ *
+ * @param len
+ * the desired string length.
+ * @return the random string.
+ * @throws NotStrictlyPositiveException if {@code len <= 0}.
+ */
+ public String nextHexString(int len) {
+ if (len <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
+ }
+
+ // Get a random number generator
+ RandomGenerator ran = getRan();
+
+ // 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);
+ }
+
+ /**
+ * Generate a random int value uniformly distributed between
+ * <code>lower</code> and <code>upper</code>, inclusive.
+ *
+ * @param lower
+ * the lower bound.
+ * @param upper
+ * the upper bound.
+ * @return the random integer.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public int nextInt(int lower, int upper) {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
+ lower, upper, false);
+ }
+ double r = getRan().nextDouble();
+ return (int) ((r * upper) + ((1.0 - r) * lower) + r);
+ }
+
+ /**
+ * Generate a random long value uniformly distributed between
+ * <code>lower</code> and <code>upper</code>, inclusive.
+ *
+ * @param lower
+ * the lower bound.
+ * @param upper
+ * the upper bound.
+ * @return the random integer.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public long nextLong(long lower, long upper) {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
+ lower, upper, false);
+ }
+ double r = getRan().nextDouble();
+ return (long) ((r * upper) + ((1.0 - r) * lower) + r);
+ }
+
+ /**
+ * {@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>
+ * <li>
+ * SHA-1 hash is applied to yield a 20-byte binary digest.</li>
+ * <li>
+ * Each byte of the binary digest is converted to 2 hex digits.</li>
+ * </ol>
+ * </p>
+ *
+ * @param len
+ * the length of the generated string
+ * @return the random string
+ * @throws NotStrictlyPositiveException if {@code len <= 0}.
+ */
+ public String nextSecureHexString(int len) {
+ if (len <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
+ }
+
+ // Get SecureRandom and setup Digest provider
+ SecureRandom 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);
+ }
+
+ /**
+ * Generate a random int value uniformly distributed between
+ * <code>lower</code> and <code>upper</code>, inclusive. This algorithm uses
+ * a secure random number generator.
+ *
+ * @param lower
+ * the lower bound.
+ * @param upper
+ * the upper bound.
+ * @return the random integer.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public int nextSecureInt(int lower, int upper) {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
+ lower, upper, false);
+ }
+ SecureRandom sec = getSecRan();
+ return lower + (int) (sec.nextDouble() * (upper - lower + 1));
+ }
+
+ /**
+ * Generate a random long value uniformly distributed between
+ * <code>lower</code> and <code>upper</code>, inclusive. This algorithm uses
+ * a secure random number generator.
+ *
+ * @param lower
+ * the lower bound.
+ * @param upper
+ * the upper bound.
+ * @return the random integer.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public long nextSecureLong(long lower, long upper) {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
+ lower, upper, false);
+ }
+ SecureRandom sec = getSecRan();
+ return lower + (long) (sec.nextDouble() * (upper - lower + 1));
+ }
+
+ /**
+ * {@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>
+ *
+ * <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.</li></ul></p>
+ *
+ * @param mean mean of the Poisson distribution.
+ * @return the random Poisson value.
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ */
+ public long nextPoisson(double mean) {
+ if (mean <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, mean);
+ }
+
+ final RandomGenerator generator = getRan();
+
+ final double pivot = 40.0d;
+ if (mean < pivot) {
+ double p = FastMath.exp(-mean);
+ long n = 0;
+ double r = 1.0d;
+ double rnd = 1.0d;
+
+ while (n < 1000 * mean) {
+ rnd = generator.nextDouble();
+ r = r * rnd;
+ if (r >= p) {
+ n++;
+ } else {
+ return n;
+ }
+ }
+ return n;
+ } else {
+ final double lambda = FastMath.floor(mean);
+ final double lambdaFractional = mean - lambda;
+ final double logLambda = FastMath.log(lambda);
+ final double logLambdaFactorial = MathUtils.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 = nextUniform(0.0, 1);
+ if (u <= p1) {
+ final double n = nextGaussian(0d, 1d);
+ 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 = nextExponential(1d);
+ v = -e - (n * n / 2) + c1;
+ } else {
+ if (u > p1 + p2) {
+ y = lambda;
+ break;
+ } else {
+ x = delta + (twolpd / delta) * nextExponential(1d);
+ y = FastMath.ceil(x);
+ v = -nextExponential(1d) - 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 - MathUtils.factorialLog((int) (y + lambda)) + logLambdaFactorial) {
+ y = lambda + y;
+ break;
+ }
+ }
+ return y2 + (long) y;
+ }
+ }
+
+ /**
+ * Generate a random value from a Normal (a.k.a. Gaussian) distribution with
+ * the given mean, <code>mu</code> and the given standard deviation,
+ * <code>sigma</code>.
+ *
+ * @param mu
+ * the mean of the distribution
+ * @param sigma
+ * the standard deviation of the distribution
+ * @return the random Normal value
+ * @throws NotStrictlyPositiveException if {@code sigma <= 0}.
+ */
+ public double nextGaussian(double mu, double sigma) {
+ if (sigma <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.STANDARD_DEVIATION, sigma);
+ }
+ return sigma * getRan().nextGaussian() + mu;
+ }
+
+ /**
+ * Returns a random value from an Exponential distribution with the given
+ * mean.
+ * <p>
+ * <strong>Algorithm Description</strong>: 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.
+ * </p>
+ *
+ * @param mean the mean of the distribution
+ * @return the random Exponential value
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ */
+ public double nextExponential(double mean) {
+ if (mean <= 0.0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, mean);
+ }
+ final RandomGenerator generator = getRan();
+ double unif = generator.nextDouble();
+ while (unif == 0.0d) {
+ unif = generator.nextDouble();
+ }
+ return -mean * FastMath.log(unif);
+ }
+
+ /**
+ * {@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).
+ * </p>
+ *
+ * @param lower
+ * the lower bound.
+ * @param upper
+ * the upper bound.
+ * @return a uniformly distributed random value from the interval (lower,
+ * upper)
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public double nextUniform(double lower, double upper) {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
+ lower, upper, false);
+ }
+ final RandomGenerator generator = getRan();
+
+ // ensure nextDouble() isn't 0.0
+ double u = generator.nextDouble();
+ while (u <= 0.0) {
+ u = generator.nextDouble();
+ }
+
+ return lower + u * (upper - lower);
+ }
+
+ /**
+ * Generates a random value from the {@link BetaDistributionImpl Beta Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextBeta(double alpha, double beta) throws MathException {
+ return nextInversionDeviate(new BetaDistributionImpl(alpha, beta));
+ }
+
+ /**
+ * Generates a random value from the {@link BinomialDistributionImpl Binomial Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public int nextBinomial(int numberOfTrials, double probabilityOfSuccess) throws MathException {
+ return nextInversionDeviate(new BinomialDistributionImpl(numberOfTrials, probabilityOfSuccess));
+ }
+
+ /**
+ * Generates a random value from the {@link CauchyDistributionImpl Cauchy Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextCauchy(double median, double scale) throws MathException {
+ return nextInversionDeviate(new CauchyDistributionImpl(median, scale));
+ }
+
+ /**
+ * Generates a random value from the {@link ChiSquaredDistributionImpl ChiSquare Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) inversion}
+ * to generate random values.
+ *
+ * @param df the degrees of freedom of the ChiSquare distribution
+ * @return random value sampled from the ChiSquare(df) distribution
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextChiSquare(double df) throws MathException {
+ return nextInversionDeviate(new ChiSquaredDistributionImpl(df));
+ }
+
+ /**
+ * Generates a random value from the {@link FDistributionImpl F Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) 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 MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextF(double numeratorDf, double denominatorDf) throws MathException {
+ return nextInversionDeviate(new FDistributionImpl(numeratorDf, denominatorDf));
+ }
+
+ /**
+ * Generates a random value from the {@link GammaDistributionImpl Gamma Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) inversion}
+ * to generate random values.
+ *
+ * @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 MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextGamma(double shape, double scale) throws MathException {
+ return nextInversionDeviate(new GammaDistributionImpl(shape, scale));
+ }
+
+ /**
+ * Generates a random value from the {@link HypergeometricDistributionImpl 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 MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public int nextHypergeometric(int populationSize, int numberOfSuccesses, int sampleSize) throws MathException {
+ return nextInversionDeviate(new HypergeometricDistributionImpl(populationSize, numberOfSuccesses, sampleSize));
+ }
+
+ /**
+ * Generates a random value from the {@link PascalDistributionImpl 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public int nextPascal(int r, double p) throws MathException {
+ return nextInversionDeviate(new PascalDistributionImpl(r, p));
+ }
+
+ /**
+ * Generates a random value from the {@link TDistributionImpl T Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) inversion}
+ * to generate random values.
+ *
+ * @param df the degrees of freedom of the T distribution
+ * @return random value from the T(df) distribution
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextT(double df) throws MathException {
+ return nextInversionDeviate(new TDistributionImpl(df));
+ }
+
+ /**
+ * Generates a random value from the {@link WeibullDistributionImpl Weibull Distribution}.
+ * This implementation uses {@link #nextInversionDeviate(ContinuousDistribution) 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public double nextWeibull(double shape, double scale) throws MathException {
+ return nextInversionDeviate(new WeibullDistributionImpl(shape, scale));
+ }
+
+ /**
+ * Generates a random value from the {@link ZipfDistributionImpl 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
+ * @throws MathException if an error occurs generating the random value
+ * @since 2.2
+ */
+ public int nextZipf(int numberOfElements, double exponent) throws MathException {
+ return nextInversionDeviate(new ZipfDistributionImpl(numberOfElements, exponent));
+ }
+
+ /**
+ * Returns the RandomGenerator used to generate non-secure random data.
+ * <p>
+ * Creates and initializes a default generator if null.
+ * </p>
+ *
+ * @return the Random used to generate random data
+ * @since 1.1
+ */
+ private RandomGenerator getRan() {
+ if (rand == null) {
+ rand = new JDKRandomGenerator();
+ rand.setSeed(System.currentTimeMillis());
+ }
+ return rand;
+ }
+
+ /**
+ * Returns the SecureRandom used to generate secure random data.
+ * <p>
+ * Creates and initializes if null.
+ * </p>
+ *
+ * @return the SecureRandom used to generate secure random data
+ */
+ private SecureRandom getSecRan() {
+ if (secRand == null) {
+ secRand = new SecureRandom();
+ secRand.setSeed(System.currentTimeMillis());
+ }
+ return secRand;
+ }
+
+ /**
+ * Reseeds the random number generator with the supplied seed.
+ * <p>
+ * Will create and initialize if null.
+ * </p>
+ *
+ * @param seed
+ * the seed value to use
+ */
+ public void reSeed(long seed) {
+ if (rand == null) {
+ rand = new JDKRandomGenerator();
+ }
+ rand.setSeed(seed);
+ }
+
+ /**
+ * Reseeds the secure random number generator with the current time in
+ * milliseconds.
+ * <p>
+ * Will create and initialize if null.
+ * </p>
+ */
+ public void reSeedSecure() {
+ if (secRand == null) {
+ secRand = new SecureRandom();
+ }
+ secRand.setSeed(System.currentTimeMillis());
+ }
+
+ /**
+ * Reseeds the secure random number generator with the supplied seed.
+ * <p>
+ * Will create and initialize if null.
+ * </p>
+ *
+ * @param seed
+ * the seed value to use
+ */
+ public void reSeedSecure(long seed) {
+ if (secRand == null) {
+ secRand = new SecureRandom();
+ }
+ secRand.setSeed(seed);
+ }
+
+ /**
+ * Reseeds the random number generator with the current time in
+ * milliseconds.
+ */
+ public void reSeed() {
+ if (rand == null) {
+ rand = new JDKRandomGenerator();
+ }
+ rand.setSeed(System.currentTimeMillis());
+ }
+
+ /**
+ * 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.
+ * </p>
+ *
+ * @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 = SecureRandom.getInstance(algorithm, provider);
+ }
+
+ /**
+ * Generates an integer array of length <code>k</code> whose entries are
+ * selected randomly, without repetition, from the integers
+ * <code>0 through n-1</code> (inclusive).
+ * <p>
+ * Generated arrays represent permutations of <code>n</code> taken
+ * <code>k</code> at a time.
+ * </p>
+ * <p>
+ * <strong>Preconditions:</strong>
+ * <ul>
+ * <li> <code>k <= n</code></li>
+ * <li> <code>n > 0</code></li>
+ * </ul>
+ * If the preconditions are not met, an IllegalArgumentException is thrown.
+ * </p>
+ * <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>.
+ * </p>
+ *
+ * @param n
+ * domain of the permutation (must be positive)
+ * @param k
+ * size of the permutation (must satisfy 0 < k <= n).
+ * @return the random permutation as an int array
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws NotStrictlyPositiveException if {@code k <= 0}.
+ */
+ public int[] nextPermutation(int n, int k) {
+ 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 = getNatural(n);
+ shuffle(index, n - k);
+ int[] result = new int[k];
+ for (int i = 0; i < k; i++) {
+ result[i] = index[n - i - 1];
+ }
+
+ return result;
+ }
+
+ /**
+ * Uses a 2-cycle permutation shuffle to generate a random permutation.
+ * <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>
+ *
+ * @param c
+ * Collection to sample from.
+ * @param k
+ * sample size.
+ * @return the random sample.
+ * @throws NumberIsTooLargeException if {@code k > c.size()}.
+ * @throws NotStrictlyPositiveException if {@code k <= 0}.
+ */
+ public Object[] nextSample(Collection<?> c, int k) {
+ 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;
+ }
+
+ /**
+ * 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 MathException if an error occurs computing the inverse cumulative distribution function
+ * @since 2.2
+ */
+ public double nextInversionDeviate(ContinuousDistribution distribution) throws MathException {
+ 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 MathException if an error occurs computing the inverse cumulative distribution function
+ * @since 2.2
+ */
+ public int nextInversionDeviate(IntegerDistribution distribution) throws MathException {
+ final double target = nextUniform(0, 1);
+ final int glb = distribution.inverseCumulativeProbability(target);
+ if (distribution.cumulativeProbability(glb) == 1.0d) { // No mass above
+ return glb;
+ } else {
+ return glb + 1;
+ }
+ }
+
+ // ------------------------Private methods----------------------------------
+
+ /**
+ * Uses a 2-cycle permutation shuffle to randomly re-order the last elements
+ * of list.
+ *
+ * @param list
+ * list to be shuffled
+ * @param end
+ * element past which shuffling begins
+ */
+ private void shuffle(int[] list, int end) {
+ int target = 0;
+ for (int i = list.length - 1; i >= end; i--) {
+ if (i == 0) {
+ target = 0;
+ } else {
+ target = nextInt(0, i);
+ }
+ int temp = list[target];
+ list[target] = list[i];
+ list[i] = temp;
+ }
+ }
+
+ /**
+ * Returns an array representing n.
+ *
+ * @param n
+ * the natural number to represent
+ * @return array with entries = elements of n
+ */
+ private int[] getNatural(int n) {
+ int[] natural = new int[n];
+ for (int i = 0; i < n; i++) {
+ natural[i] = i;
+ }
+ return natural;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/RandomGenerator.java b/src/main/java/org/apache/commons/math/random/RandomGenerator.java
new file mode 100644
index 0000000..0b86c2a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/RandomGenerator.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.math.random;
+
+
+/**
+ * Interface extracted from <code>java.util.Random</code>. This interface is
+ * implemented by {@link AbstractRandomGenerator}.
+ *
+ * @since 1.1
+ * @version $Revision: 949750 $ $Date: 2010-05-31 16:06:04 +0200 (lun. 31 mai 2010) $
+ */
+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.
+ * </p>
+ * @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.
+ * </p>
+ * @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.
+ * </p>
+ * @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 <tt>int</tt> 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 <tt>int</tt> 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 <tt>int</tt>
+ * 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 <tt>long</tt> 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/math/random/RandomVectorGenerator.java b/src/main/java/org/apache/commons/math/random/RandomVectorGenerator.java
new file mode 100644
index 0000000..15abbd7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/RandomVectorGenerator.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.math.random;
+
+
+/** This interface represents a random generator for whole vectors.
+ *
+ * @since 1.2
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ *
+ */
+
+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/math/random/UncorrelatedRandomVectorGenerator.java b/src/main/java/org/apache/commons/math/random/UncorrelatedRandomVectorGenerator.java
new file mode 100644
index 0000000..d365f9b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/UncorrelatedRandomVectorGenerator.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.math.random;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+
+/**
+ * A {@link RandomVectorGenerator} that generates vectors with uncorrelated
+ * components. Components of generated vectors follow (independent) Gaussian
+ * distributions, with parameters supplied in the constructor.
+ *
+ * @version $Revision: 962515 $ $Date: 2010-07-09 15:15:28 +0200 (ven. 09 juil. 2010) $
+ * @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.</p>
+ * @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</p>
+ * @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/math/random/UniformRandomGenerator.java b/src/main/java/org/apache/commons/math/random/UniformRandomGenerator.java
new file mode 100644
index 0000000..54492d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/UniformRandomGenerator.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.math.random;
+
+import org.apache.commons.math.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].</p>
+ *
+ * @since 1.2
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+
+public class UniformRandomGenerator implements NormalizedRandomGenerator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1569292426375546027L;
+
+ /** 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).</p>
+ * @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/math/random/UnitSphereRandomVectorGenerator.java b/src/main/java/org/apache/commons/math/random/UnitSphereRandomVectorGenerator.java
new file mode 100644
index 0000000..cac8f18
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/UnitSphereRandomVectorGenerator.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.math.random;
+
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * Generate random vectors isotropically located on the surface of a sphere.
+ *
+ * @since 2.1
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+
+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];
+
+ double normSq;
+ do {
+ normSq = 0;
+ for (int i = 0; i < dimension; i++) {
+ final double comp = 2 * rand.nextDouble() - 1;
+ v[i] = comp;
+ normSq += comp * comp;
+ }
+ } while (normSq > 1);
+
+ 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/math/random/ValueServer.java b/src/main/java/org/apache/commons/math/random/ValueServer.java
new file mode 100644
index 0000000..9146e69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/ValueServer.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.math.random;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * Generates values for use in simulation applications.
+ * <p>
+ * How values are generated is determined by the <code>mode</code>
+ * property.</p>
+ * <p>
+ * Supported <code>mode</code> values are: <ul>
+ * <li> DIGEST_MODE -- uses an empirical distribution </li>
+ * <li> REPLAY_MODE -- replays data from <code>valuesFileURL</code></li>
+ * <li> UNIFORM_MODE -- generates uniformly distributed random values with
+ * mean = <code>mu</code> </li>
+ * <li> EXPONENTIAL_MODE -- generates exponentially distributed random values
+ * with mean = <code>mu</code></li>
+ * <li> GAUSSIAN_MODE -- generates Gaussian distributed random values with
+ * mean = <code>mu</code> and
+ * standard deviation = <code>sigma</code></li>
+ * <li> CONSTANT_MODE -- returns <code>mu</code> every time.</li></ul></p>
+ *
+ * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
+ *
+ */
+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 RandomData randomData;
+
+ // Data generation modes ======================================
+
+ /** Creates new ValueServer */
+ public ValueServer() {
+ randomData = new RandomDataImpl();
+ }
+
+ /**
+ * Construct a ValueServer instance using a RandomData as its source
+ * of random data.
+ *
+ * @param randomData the RandomData instance used to source random data
+ * @since 1.1
+ */
+ public ValueServer(RandomData randomData) {
+ this.randomData = randomData;
+ }
+
+ /**
+ * 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
+ */
+ public double getNext() throws IOException {
+ 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 MathRuntimeException.createIllegalStateException(
+ 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
+ */
+ public void fill(double[] values) throws IOException {
+ 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
+ */
+ public double[] fill(int length) throws IOException {
+ 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>
+ * <p>
+ * This method must be called before using <code>getNext()</code>
+ * with <code>mode = DIGEST_MODE</code></p>
+ *
+ * @throws IOException if an I/O error occurs reading the input file
+ */
+ public void computeDistribution() throws IOException {
+ empiricalDistribution = new EmpiricalDistributionImpl();
+ empiricalDistribution.load(valuesFileURL);
+ }
+
+ /**
+ * 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>
+ * <p>
+ * This method must be called before using <code>getNext()</code>
+ * with <code>mode = DIGEST_MODE</code></p>
+ *
+ * @param binCount the number of bins used in computing the empirical
+ * distribution
+ * @throws IOException if an error occurs reading the input file
+ */
+ public void computeDistribution(int binCount)
+ throws IOException {
+ empiricalDistribution = new EmpiricalDistributionImpl(binCount);
+ empiricalDistribution.load(valuesFileURL);
+ mu = empiricalDistribution.getSampleStats().getMean();
+ sigma = empiricalDistribution.getSampleStats().getStandardDeviation();
+ }
+
+ /** Getter for property mode.
+ * @return Value of property mode.
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /** Setter for property mode.
+ * @param mode New value of property mode.
+ */
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Getter for <code>valuesFileURL<code>
+ * @return Value of property valuesFileURL.
+ */
+ public URL getValuesFileURL() {
+ return valuesFileURL;
+ }
+
+ /**
+ * Sets the <code>valuesFileURL</code> 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 <code>valuesFileURL</code>
+ * @param url New value of property valuesFileURL.
+ */
+ public void setValuesFileURL(URL url) {
+ this.valuesFileURL = url;
+ }
+
+ /** Getter for property empiricalDistribution.
+ * @return Value of property empiricalDistribution.
+ */
+ 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
+ */
+ public void resetReplayFile() throws IOException {
+ if (filePointer != null) {
+ try {
+ filePointer.close();
+ filePointer = null;
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ filePointer = new BufferedReader(new InputStreamReader(valuesFileURL.openStream()));
+ }
+
+ /**
+ * Closes <code>valuesFileURL</code> 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;
+ }
+ }
+
+ /** Getter for property mu.
+ * @return Value of property mu.
+ */
+ public double getMu() {
+ return mu;
+ }
+
+ /** Setter for property mu.
+ * @param mu New value of property mu.
+ */
+ public void setMu(double mu) {
+ this.mu = mu;
+ }
+
+ /** Getter for property sigma.
+ * @return Value of property sigma.
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+ /** Setter for property sigma.
+ * @param sigma New value of property sigma.
+ */
+ public void setSigma(double sigma) {
+ this.sigma = sigma;
+ }
+
+ //------------- 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</li></ul></p>
+ *
+ * @return next random value from the empirical distribution digest
+ */
+ private double getNextDigest() {
+ if ((empiricalDistribution == null) ||
+ (empiricalDistribution.getBinStats().size() == 0)) {
+ throw MathRuntimeException.createIllegalStateException(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>
+ * <p>
+ * This method will open the <code>valuesFileURL</code> if there is no
+ * replay file open.</p>
+ * <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.</p>
+ *
+ * @return next value from the replay file
+ * @throws IOException if there is a problem reading from the file
+ * @throws NumberFormatException if an invalid numeric string is
+ * encountered in the file
+ */
+ private double getNextReplay() throws IOException {
+ 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 MathRuntimeException.createEOFException(LocalizedFormats.URL_CONTAINS_NO_DATA,
+ valuesFileURL);
+ }
+ }
+ return Double.valueOf(str).doubleValue();
+ }
+
+ /**
+ * Gets a uniformly distributed random value with mean = mu.
+ *
+ * @return random uniform value
+ */
+ private double getNextUniform() {
+ return randomData.nextUniform(0, 2 * mu);
+ }
+
+ /**
+ * Gets an exponentially distributed random value with mean = mu.
+ *
+ * @return random exponential value
+ */
+ private double getNextExponential() {
+ return randomData.nextExponential(mu);
+ }
+
+ /**
+ * Gets a Gaussian distributed random value with mean = mu
+ * and standard deviation = sigma.
+ *
+ * @return random Gaussian value
+ */
+ private double getNextGaussian() {
+ return randomData.nextGaussian(mu, sigma);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/Well1024a.java b/src/main/java/org/apache/commons/math/random/Well1024a.java
new file mode 100644
index 0000000..8406ed5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well1024a.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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/math/random/Well19937a.java b/src/main/java/org/apache/commons/math/random/Well19937a.java
new file mode 100644
index 0000000..97f9bfd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well19937a.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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/math/random/Well19937c.java b/src/main/java/org/apache/commons/math/random/Well19937c.java
new file mode 100644
index 0000000..53029c1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well19937c.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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 ^ ((z4 << 7) & 0xe46e1700);
+ z4 = z4 ^ ((z4 << 15) & 0x9b868000);
+
+ return z4 >>> (32 - bits);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/Well44497a.java b/src/main/java/org/apache/commons/math/random/Well44497a.java
new file mode 100644
index 0000000..70d672c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well44497a.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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/math/random/Well44497b.java b/src/main/java/org/apache/commons/math/random/Well44497b.java
new file mode 100644
index 0000000..28b5dbb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well44497b.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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 ^ ((z4 << 7) & 0x93dd1400);
+ z4 = z4 ^ ((z4 << 15) & 0xfa118000);
+
+ return z4 >>> (32 - bits);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/random/Well512a.java b/src/main/java/org/apache/commons/math/random/Well512a.java
new file mode 100644
index 0000000..c822c9d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/Well512a.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.math.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>.</p>
+
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a>
+ * @version $Revision: 1003892 $ $Date: 2010-10-02 23:28:56 +0200 (sam. 02 oct. 2010) $
+ * @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.</p>
+ */
+ 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/math/random/package.html b/src/main/java/org/apache/commons/math/random/package.html
new file mode 100644
index 0000000..9978ca0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/random/package.html
@@ -0,0 +1,132 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $ -->
+ <body>
+ <p>Random number and random data generators.</p>
+ <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.math.random.JDKRandomGenerator JDKRandomGenerator}
+ that extends the JDK provided generator</li>
+ <li>AbstractRandomGenerator as a helper for users generators</li>
+ <li>BitStreamGenerator which is an abstract class for several generators and
+ which in turn is extended by:
+ <ul>
+ <li>{@link org.apache.commons.math.random.MersenneTwister MersenneTwister}</li>
+ <li>{@link org.apache.commons.math.random.Well512a Well512a}</li>
+ <li>{@link org.apache.commons.math.random.Well1024a Well1024a}</li>
+ <li>{@link org.apache.commons.math.random.Well19937a Well19937a}</li>
+ <li>{@link org.apache.commons.math.random.Well19937c Well19937c}</li>
+ <li>{@link org.apache.commons.math.random.Well44497a Well44497a}</li>
+ <li>{@link org.apache.commons.math.random.Well44497b Well44497b}</li>
+ </ul>
+ </li>
+ </ul>
+ </p>
+
+ <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>
+
+ <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>
+
+ <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>
+
+ <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>
+
+ <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>
+
+ <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.math.random.MersenneTwister MersenneTwister}</td><td>1</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.JDKRandomGenerator JDKRandomGenerator}</td><td>between 0.96 and 1.16</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well512a Well512a}</td><td>between 0.85 and 0.88</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well1024a Well1024a}</td><td>between 0.63 and 0.73</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well19937a Well19937a}</td><td>between 0.70 and 0.71</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well19937c Well19937c}</td><td>between 0.57 and 0.71</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well44497a Well44497a}</td><td>between 0.69 and 0.71</td></tr>
+ <tr><td>{@link org.apache.commons.math.random.Well44497b Well44497b}</td><td>between 0.65 and 0.71</td></tr>
+ </table>
+ </p>
+
+ <p>
+ So for most simulation problems, the better generators like {@link
+ org.apache.commons.math.random.Well19937c Well19937c} and {@link
+ org.apache.commons.math.random.Well44497b Well44497b} are probably very good choices.
+ </p>
+
+ <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.
+ </p>
+
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/special/Beta.java b/src/main/java/org/apache/commons/math/special/Beta.java
new file mode 100644
index 0000000..6ab284f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/special/Beta.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.math.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.ContinuedFraction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This is a utility class that provides computation methods related to the
+ * Beta family of functions.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class Beta {
+
+ /** Maximum allowed numerical error. */
+ private static final double DEFAULT_EPSILON = 10e-15;
+
+ /**
+ * Default constructor. Prohibit instantiation.
+ */
+ private Beta() {
+ super();
+ }
+
+ /**
+ * Returns the
+ * <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">
+ * regularized beta function</a> I(x, a, b).
+ *
+ * @param x the value.
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @return the regularized beta function I(x, a, b)
+ * @throws MathException if the algorithm fails to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b)
+ throws MathException
+ {
+ 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 the value.
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @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 MathException if the algorithm fails to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b,
+ double epsilon) throws MathException
+ {
+ return regularizedBeta(x, a, b, epsilon, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the regularized beta function I(x, a, b).
+ *
+ * @param x the value.
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @param maxIterations Maximum number of "iterations" to complete.
+ * @return the regularized beta function I(x, a, b)
+ * @throws MathException if the algorithm fails to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b,
+ int maxIterations) throws MathException
+ {
+ return regularizedBeta(x, a, b, DEFAULT_EPSILON, maxIterations);
+ }
+
+ /**
+ * Returns the regularized beta function I(x, a, b).
+ *
+ * The implementation of this method is based on:
+ * <ul>
+ * <li>
+ * <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">
+ * Regularized Beta Function</a>.</li>
+ * <li>
+ * <a href="http://functions.wolfram.com/06.21.10.0001.01">
+ * Regularized Beta Function</a>.</li>
+ * </ul>
+ *
+ * @param x the value.
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @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 MathException if the algorithm fails to converge.
+ */
+ public static double regularizedBeta(double x, final double a,
+ final double b, double epsilon, int maxIterations) throws MathException
+ {
+ double ret;
+
+ if (Double.isNaN(x) || Double.isNaN(a) || Double.isNaN(b) || (x < 0) ||
+ (x > 1) || (a <= 0.0) || (b <= 0.0))
+ {
+ ret = Double.NaN;
+ } else if (x > (a + 1.0) / (a + b + 2.0)) {
+ ret = 1.0 - regularizedBeta(1.0 - x, b, a, epsilon, maxIterations);
+ } else {
+ ContinuedFraction fraction = new ContinuedFraction() {
+
+ @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;
+ }
+
+ @Override
+ protected double getA(int n, double x) {
+ return 1.0;
+ }
+ };
+ ret = FastMath.exp((a * FastMath.log(x)) + (b * FastMath.log(1.0 - x)) -
+ FastMath.log(a) - logBeta(a, b, epsilon, maxIterations)) *
+ 1.0 / fraction.evaluate(x, epsilon, maxIterations);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the natural logarithm of the beta function B(a, b).
+ *
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @return log(B(a, b))
+ */
+ public static double logBeta(double a, double b) {
+ return logBeta(a, b, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the natural logarithm of the beta function B(a, b).
+ *
+ * The implementation of this method is based on:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/BetaFunction.html">
+ * Beta Function</a>, equation (1).</li>
+ * </ul>
+ *
+ * @param a the a parameter.
+ * @param b the b parameter.
+ * @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 log(B(a, b))
+ */
+ public static double logBeta(double a, double b, double epsilon,
+ int maxIterations) {
+
+ double ret;
+
+ if (Double.isNaN(a) || Double.isNaN(b) || (a <= 0.0) || (b <= 0.0)) {
+ ret = Double.NaN;
+ } else {
+ ret = Gamma.logGamma(a) + Gamma.logGamma(b) -
+ Gamma.logGamma(a + b);
+ }
+
+ return ret;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/special/Erf.java b/src/main/java/org/apache/commons/math/special/Erf.java
new file mode 100644
index 0000000..5abe18b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/special/Erf.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.math.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This is a utility class that provides computation methods related to the
+ * error functions.
+ *
+ * @version $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $
+ */
+public class Erf {
+
+ /**
+ * Default constructor. Prohibit instantiation.
+ */
+ private Erf() {
+ super();
+ }
+
+ /**
+ * <p>Returns the error function</p>
+ * <p>erf(x) = 2/&radic;&pi; <sub>0</sub>&int;<sup>x</sup> e<sup>-t<sup>2</sup></sup>dt </p>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @param x the value.
+ * @return the error function erf(x)
+ * @throws MathException if the algorithm fails to converge.
+ * @see Gamma#regularizedGammaP(double, double, double, int)
+ */
+ public static double erf(double x) throws MathException {
+ if (FastMath.abs(x) > 40) {
+ return x > 0 ? 1 : -1;
+ }
+ double ret = Gamma.regularizedGammaP(0.5, x * x, 1.0e-15, 10000);
+ if (x < 0) {
+ ret = -ret;
+ }
+ return ret;
+ }
+
+ /**
+ * <p>Returns the complementary error function</p>
+ * <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>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @param x the value
+ * @return the complementary error function erfc(x)
+ * @throws MathException if the algorithm fails to converge
+ * @see Gamma#regularizedGammaQ(double, double, double, int)
+ * @since 2.2
+ */
+ public static double erfc(double x) throws MathException {
+ 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;
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math/special/Gamma.java b/src/main/java/org/apache/commons/math/special/Gamma.java
new file mode 100644
index 0000000..327aa3a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/special/Gamma.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.util.ContinuedFraction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This is a utility class that provides computation methods related to the
+ * Gamma family of functions.
+ *
+ * @version $Revision: 1042510 $ $Date: 2010-12-06 02:54:18 +0100 (lun. 06 déc. 2010) $
+ */
+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;
+
+ /** 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);
+
+ // 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;
+
+ /**
+ * Default constructor. Prohibit instantiation.
+ */
+ private Gamma() {
+ super();
+ }
+
+ /**
+ * Returns the natural logarithm of the gamma function &#915;(x).
+ *
+ * The implementation of this method is based on:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/GammaFunction.html">
+ * Gamma Function</a>, equation (28).</li>
+ * <li><a href="http://mathworld.wolfram.com/LanczosApproximation.html">
+ * Lanczos Approximation</a>, equations (1) through (5).</li>
+ * <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></li>
+ * </ul>
+ *
+ * @param x the value.
+ * @return log(&#915;(x))
+ */
+ public static double logGamma(double x) {
+ double ret;
+
+ if (Double.isNaN(x) || (x <= 0.0)) {
+ ret = Double.NaN;
+ } else {
+ double g = 607.0 / 128.0;
+
+ double sum = 0.0;
+ for (int i = LANCZOS.length - 1; i > 0; --i) {
+ sum = sum + (LANCZOS[i] / (x + i));
+ }
+ sum = sum + LANCZOS[0];
+
+ double tmp = x + 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 the a parameter.
+ * @param x the value.
+ * @return the regularized gamma function P(a, x)
+ * @throws MathException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaP(double a, double x)
+ throws MathException
+ {
+ return regularizedGammaP(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+
+ /**
+ * Returns the regularized gamma function P(a, x).
+ *
+ * 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>
+ * <li>
+ * <a href="http://mathworld.wolfram.com/IncompleteGammaFunction.html">
+ * Incomplete Gamma Function</a>, equation (4).</li>
+ * <li>
+ * <a href="http://mathworld.wolfram.com/ConfluentHypergeometricFunctionoftheFirstKind.html">
+ * Confluent Hypergeometric Function of the First Kind</a>, equation (1).
+ * </li>
+ * </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 MathException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaP(double a,
+ double x,
+ double epsilon,
+ int maxIterations)
+ throws MathException
+ {
+ 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 = n + 1.0;
+ an = an * (x / (a + n));
+
+ // update partial sum
+ sum = sum + an;
+ }
+ if (n >= maxIterations) {
+ throw new MaxIterationsExceededException(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 MathException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaQ(double a, double x)
+ throws MathException
+ {
+ return regularizedGammaQ(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the regularized gamma function Q(a, x) = 1 - P(a, x).
+ *
+ * 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>
+ * <li>
+ * <a href="http://functions.wolfram.com/GammaBetaErf/GammaRegularized/10/0003/">
+ * Regularized incomplete gamma function: Continued fraction representations (formula 06.08.10.0003)</a></li>
+ * </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 MathException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaQ(final double a,
+ double x,
+ double epsilon,
+ int maxIterations)
+ throws MathException
+ {
+ 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() {
+
+ @Override
+ protected double getA(int n, double x) {
+ return ((2.0 * n) + 1.0) - a + x;
+ }
+
+ @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;
+ }
+
+
+ /**
+ * <p>Computes the digamma function of x.</p>
+ *
+ * <p>This is an independently written implementation of the algorithm described in
+ * Jose Bernardo, Algorithm AS 103: Psi (Digamma) Function, Applied Statistics, 1976.</p>
+ *
+ * <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>
+ *
+ * <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.</p>
+ *
+ * @param x the 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 at wikipedia </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 (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;
+ }
+
+ /**
+ * <p>Computes the trigamma function of x. This function is derived by taking the derivative of
+ * the implementation of digamma.</p>
+ *
+ * @param x the 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 at wikipedia </a>
+ * @see Gamma#digamma(double)
+ * @since 2.0
+ */
+ public static double trigamma(double 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);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/special/package.html b/src/main/java/org/apache/commons/math/special/package.html
new file mode 100644
index 0000000..0676334
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/special/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Implementations of special functions such as Beta and Gamma.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/Frequency.java b/src/main/java/org/apache/commons/math/stat/Frequency.java
new file mode 100644
index 0000000..434819e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/Frequency.java
@@ -0,0 +1,603 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat;
+
+import java.io.Serializable;
+import java.text.NumberFormat;
+import java.util.Iterator;
+import java.util.Comparator;
+import java.util.TreeMap;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ * <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>
+ * <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>
+ * <p>
+ * The values are ordered using the default (natural order), unless a
+ * <code>Comparator</code> is supplied in the constructor.</p>
+ *
+ * @version $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $
+ */
+public class Frequency implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3845586908418844111L;
+
+ /** underlying collection */
+ private final TreeMap<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.
+ * </p>
+ *
+ * @param v the value to add.
+ * @throws IllegalArgumentException if <code>v</code> is not Comparable,
+ * or is not comparable with previous entries
+ * @deprecated use {@link #addValue(Comparable)} instead
+ */
+ @Deprecated
+ public void addValue(Object v) {
+ if (v instanceof Comparable<?>){
+ addValue((Comparable<?>) v);
+ } else {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CLASS_DOESNT_IMPLEMENT_COMPARABLE,
+ v.getClass().getName());
+ }
+ }
+
+ /**
+ * 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.
+ * </p>
+ *
+ * @param v the value to add.
+ * @throws IllegalArgumentException if <code>v</code> is not comparable with previous entries
+ */
+ public void addValue(Comparable<?> v){
+ 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(1));
+ } else {
+ freqTable.put(obj, Long.valueOf(count.longValue() + 1));
+ }
+ } catch (ClassCastException ex) {
+ //TreeMap will throw ClassCastException if v is not comparable
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSTANCES_NOT_COMPARABLE_TO_EXISTING_VALUES,
+ v.getClass().getName());
+ }
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ */
+ public void addValue(int v) {
+ addValue(Long.valueOf(v));
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ * @deprecated to be removed in math 3.0
+ */
+ @Deprecated
+ public void addValue(Integer v) {
+ addValue(Long.valueOf(v.longValue()));
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ */
+ public void addValue(long v) {
+ addValue(Long.valueOf(v));
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ */
+ public void addValue(char v) {
+ addValue(Character.valueOf(v));
+ }
+
+ /** 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.</p>
+ *
+ * @return values Iterator
+ */
+ public Iterator<Comparable<?>> valuesIterator() {
+ return freqTable.keySet().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 = v.
+ * Returns 0 if the value is not comparable.
+ *
+ * @param v the value to lookup.
+ * @return the frequency of v.
+ * @deprecated replaced by {@link #getCount(Comparable)} as of 2.0
+ */
+ @Deprecated
+ public long getCount(Object v) {
+ return getCount((Comparable<?>) v);
+ }
+
+ /**
+ * Returns the number of values = 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) {
+ // ignore and return 0 -- ClassCastException will be thrown if value is not comparable
+ }
+ return result;
+ }
+
+ /**
+ * Returns the number of values = 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 = 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 = 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.</p>
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ * @deprecated replaced by {@link #getPct(Comparable)} as of 2.0
+ */
+ @Deprecated
+ public double getPct(Object v) {
+ return getPct((Comparable<?>) v);
+ }
+
+ /**
+ * 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.</p>
+ *
+ * @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.</p>
+ *
+ * @param v the value to lookup.
+ * @return the proportion of values equal to v
+ * @deprecated replaced by {@link #getCumFreq(Comparable)} as of 2.0
+ */
+ @Deprecated
+ public long getCumFreq(Object v) {
+ return getCumFreq((Comparable<?>) 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.</p>
+ *
+ * @param v the value to lookup.
+ * @return the proportion of values equal to v
+ */
+ public long getCumFreq(Comparable<?> v) {
+ if (getSumFreq() == 0) {
+ return 0;
+ }
+ if (v instanceof Integer) {
+ return getCumFreq(((Integer) v).longValue());
+ }
+ @SuppressWarnings("unchecked") // OK, freqTable is Comparable<?>
+ 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.</p>
+ *
+ * @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.</p>
+ *
+ * @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.</p>
+ *
+ * @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.</p>
+ *
+ * @param v the value to lookup
+ * @return the proportion of values less than or equal to v
+ * @deprecated replaced by {@link #getCumPct(Comparable)} as of 2.0
+ */
+ @Deprecated
+ public double getCumPct(Object v) {
+ return getCumPct((Comparable<?>) 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.</p>
+ *
+ * @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.</p>
+ *
+ * @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.</p>
+ *
+ * @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.</p>
+ *
+ * @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));
+ }
+
+ /**
+ * A Comparator that compares comparable objects using the
+ * natural order. Copied from Commons Collections ComparableComparator.
+ */
+ 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/math/stat/StatUtils.java b/src/main/java/org/apache/commons/math/stat/StatUtils.java
new file mode 100644
index 0000000..7ae1e17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/StatUtils.java
@@ -0,0 +1,663 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+import org.apache.commons.math.stat.descriptive.rank.Max;
+import org.apache.commons.math.stat.descriptive.rank.Min;
+import org.apache.commons.math.stat.descriptive.rank.Percentile;
+import org.apache.commons.math.stat.descriptive.summary.Product;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
+
+/**
+ * StatUtils provides static methods for computing statistics based on data
+ * stored in double[] arrays.
+ *
+ * @version $Revision: 1073276 $ $Date: 2011-02-22 10:34:52 +0100 (mar. 22 févr. 2011) $
+ */
+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.</p>
+ *
+ * @param values array of values to sum
+ * @return the sum of the values or <code>Double.NaN</code> if the array
+ * is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double sum(final double[] values) {
+ 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.</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 Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ 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.</p>
+ *
+ * @param values input array
+ * @return the sum of the squared values or <code>Double.NaN</code> if the
+ * array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double sumSq(final double[] values) {
+ 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.</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 Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ 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.</p>
+ *
+ * @param values the input array
+ * @return the product of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double product(final double[] values) {
+ 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.</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 Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.summary.SumOfLogs}.
+ * </p>
+ *
+ * @param values the input array
+ * @return the sum of the natural logs of the values or Double.NaN if
+ * the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double sumLog(final double[] values) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.summary.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 Double.NaN if
+ * length = 0
+ * @throws IllegalArgumentException 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) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.Mean} for
+ * details on the computing algorithm.</p>
+ *
+ * @param values the input array
+ * @return the mean of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double mean(final double[] values) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.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 IllegalArgumentException 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) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.GeometricMean}
+ * for details on the computing algorithm.</p>
+ *
+ * @param values the input array
+ * @return the geometric mean of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double geometricMean(final double[] values) {
+ 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>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.GeometricMean}
+ * 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 geometric mean of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ 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>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.Variance} for
+ * details on the computing algorithm.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @return the variance of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double variance(final double[] values) {
+ 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>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.Variance} for
+ * details on the computing algorithm.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null or the
+ * array index parameters are not valid.</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 IllegalArgumentException 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) {
+ 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>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.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>IllegalArgumentException</code> if the array is null or the
+ * array index parameters are not valid.</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 IllegalArgumentException 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) {
+ 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>
+ * See {@link org.apache.commons.math.stat.descriptive.moment.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>IllegalArgumentException</code> if the array is null.</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 IllegalArgumentException if the array is null
+ */
+ public static double variance(final double[] values, final double mean) {
+ return VARIANCE.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>IllegalArgumentException</code> if the array is null.</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
+ * @return the maximum of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double max(final double[] values) {
+ 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>IllegalArgumentException</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 IllegalArgumentException 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) {
+ 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>IllegalArgumentException</code> if the array is null.</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
+ * @return the minimum of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public static double min(final double[] values) {
+ 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>IllegalArgumentException</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 IllegalArgumentException 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) {
+ 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></p>
+ * <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>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)</li>
+ * </ul></p>
+ * <p>
+ * See {@link org.apache.commons.math.stat.descriptive.rank.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 IllegalArgumentException if <code>values</code> is null
+ * or p is invalid
+ */
+ public static double percentile(final double[] values, final double p) {
+ 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>
+ * <li>Returns (for any value of <code>p</code>) <code>values[begin]</code>
+ * if <code>length = 1 </code></li>
+ * <li>Throws <code>IllegalArgumentException</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 org.apache.commons.math.stat.descriptive.rank.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 IllegalArgumentException 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) {
+ 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 IllegalArgumentException if the arrays do not have the same
+ * (positive) length
+ */
+ public static double sumDifference(final double[] sample1, final double[] sample2)
+ throws IllegalArgumentException {
+ int n = sample1.length;
+ if (n != sample2.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, n, sample2.length);
+ }
+ if (n < 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, sample2.length, 1);
+ }
+ 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 IllegalArgumentException if the arrays do not have the same
+ * (positive) length
+ */
+ public static double meanDifference(final double[] sample1, final double[] sample2)
+ throws IllegalArgumentException {
+ 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 IllegalArgumentException if the arrays do not have the same
+ * length or their common length is less than 2.
+ */
+ public static double varianceDifference(final double[] sample1, final double[] sample2,
+ double meanDifference) throws IllegalArgumentException {
+ double sum1 = 0d;
+ double sum2 = 0d;
+ double diff = 0d;
+ int n = sample1.length;
+ if (n != sample2.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, n, sample2.length);
+ }
+ if (n < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, n, 2);
+ }
+ 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 series, so in the end it is having 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;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/clustering/Cluster.java b/src/main/java/org/apache/commons/math/stat/clustering/Cluster.java
new file mode 100644
index 0000000..f4913d3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/clustering/Cluster.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.math.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
+ * @version $Revision: 771076 $ $Date: 2009-05-03 18:28:48 +0200 (dim. 03 mai 2009) $
+ * @since 2.0
+ */
+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/math/stat/clustering/Clusterable.java b/src/main/java/org/apache/commons/math/stat/clustering/Clusterable.java
new file mode 100644
index 0000000..65132e6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/clustering/Clusterable.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.math.stat.clustering;
+
+import java.util.Collection;
+
+/**
+ * Interface for points that can be clustered together.
+ * @param <T> the type of point that can be clustered
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 2.0
+ */
+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/math/stat/clustering/EuclideanIntegerPoint.java b/src/main/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPoint.java
new file mode 100644
index 0000000..7fec0ff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPoint.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.math.stat.clustering;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * A simple implementation of {@link Clusterable} for points with integer coordinates.
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ * @since 2.0
+ */
+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 MathUtils.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;
+ }
+ final int[] otherPoint = ((EuclideanIntegerPoint) other).getPoint();
+ if (point.length != otherPoint.length) {
+ return false;
+ }
+ for (int i = 0; i < point.length; i++) {
+ if (point[i] != otherPoint[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+ for (Integer i : point) {
+ hashCode += i.hashCode() * 13 + 7;
+ }
+ return hashCode;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 2.1
+ */
+ @Override
+ public String toString() {
+ final StringBuilder buff = new StringBuilder("(");
+ final int[] coordinates = getPoint();
+ for (int i = 0; i < coordinates.length; i++) {
+ buff.append(coordinates[i]);
+ if (i < coordinates.length - 1) {
+ buff.append(",");
+ }
+ }
+ buff.append(")");
+ return buff.toString();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.java b/src/main/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.java
new file mode 100644
index 0000000..eb61866
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.commons.math.exception.ConvergenceException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+
+/**
+ * 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>
+ * @version $Revision: 1054333 $ $Date: 2011-01-02 01:34:58 +0100 (dim. 02 janv. 2011) $
+ * @since 2.0
+ */
+public class KMeansPlusPlusClusterer<T extends Clusterable<T>> {
+
+ /** Strategies to use for replacing an empty cluster. */
+ public static 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 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
+ */
+ public List<Cluster<T>> cluster(final Collection<T> points,
+ final int k, final int maxIterations) {
+ // create the initial clusters
+ List<Cluster<T>> clusters = chooseInitialCenters(points, k, random);
+ assignPointsToClusters(clusters, points);
+
+ // 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 clusteringChanged = 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);
+ }
+ clusteringChanged = true;
+ } else {
+ newCenter = cluster.getCenter().centroidOf(cluster.getPoints());
+ if (!newCenter.equals(cluster.getCenter())) {
+ clusteringChanged = true;
+ }
+ }
+ newClusters.add(new Cluster<T>(newCenter));
+ }
+ if (!clusteringChanged) {
+ return clusters;
+ }
+ assignPointsToClusters(newClusters, points);
+ clusters = newClusters;
+ }
+ 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
+ */
+ private static <T extends Clusterable<T>> void
+ assignPointsToClusters(final Collection<Cluster<T>> clusters, final Collection<T> points) {
+ for (final T p : points) {
+ Cluster<T> cluster = getNearestCluster(clusters, p);
+ cluster.addPoint(p);
+ }
+ }
+
+ /**
+ * 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) {
+
+ final List<T> pointSet = new ArrayList<T>(points);
+ final List<Cluster<T>> resultSet = new ArrayList<Cluster<T>>();
+
+ // Choose one center uniformly at random from among the data points.
+ final T firstPoint = pointSet.remove(random.nextInt(pointSet.size()));
+ resultSet.add(new Cluster<T>(firstPoint));
+
+ final double[] dx2 = new double[pointSet.size()];
+ while (resultSet.size() < k) {
+ // For each data point x, compute D(x), the distance between x and
+ // the nearest center that has already been chosen.
+ int sum = 0;
+ for (int i = 0; i < pointSet.size(); i++) {
+ final T p = pointSet.get(i);
+ final Cluster<T> nearest = getNearestCluster(resultSet, p);
+ final double d = p.distanceFrom(nearest.getCenter());
+ sum += d * d;
+ dx2[i] = sum;
+ }
+
+ // 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() * sum;
+ for (int i = 0 ; i < dx2.length; i++) {
+ if (dx2[i] >= r) {
+ final T p = pointSet.remove(i);
+ resultSet.add(new Cluster<T>(p));
+ 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
+ */
+ private T getPointFromLargestVarianceCluster(final Collection<Cluster<T>> clusters) {
+
+ 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
+ */
+ private T getPointFromLargestNumberCluster(final Collection<Cluster<T>> clusters) {
+
+ 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
+ */
+ private T getFarthestPoint(final Collection<Cluster<T>> clusters) {
+
+ 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 nearest {@link Cluster} to the given point
+ */
+ private static <T extends Clusterable<T>> Cluster<T>
+ getNearestCluster(final Collection<Cluster<T>> clusters, final T point) {
+ double minDistance = Double.MAX_VALUE;
+ Cluster<T> minCluster = null;
+ for (final Cluster<T> c : clusters) {
+ final double distance = point.distanceFrom(c.getCenter());
+ if (distance < minDistance) {
+ minDistance = distance;
+ minCluster = c;
+ }
+ }
+ return minCluster;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/clustering/package.html b/src/main/java/org/apache/commons/math/stat/clustering/package.html
new file mode 100644
index 0000000..21e9079
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/clustering/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 770979 $ $Date: 2009-05-02 21:34:51 +0200 (sam. 02 mai 2009) $ -->
+ <body>Clustering algorithms</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/correlation/Covariance.java b/src/main/java/org/apache/commons/math/stat/correlation/Covariance.java
new file mode 100644
index 0000000..393a02d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/correlation/Covariance.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.math.stat.correlation;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ * @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 two columns
+ * and two rows.</p>
+ *
+ * @param data rectangular array with columns representing covariates
+ * @param biasCorrected true means covariances are bias-corrected
+ * @throws IllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and two columns.
+ */
+ public Covariance(double[][] data, boolean biasCorrected) {
+ 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 two columns
+ * and two rows</p>
+ *
+ * @param data rectangular array with columns representing covariates
+ * @throws IllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and two columns.
+ */
+ public Covariance(double[][] data) {
+ 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 two columns and two rows</p>
+ *
+ * @param matrix matrix with columns representing covariates
+ * @param biasCorrected true means covariances are bias-corrected
+ * @throws IllegalArgumentException if the input matrix does not have
+ * at least two rows and two columns
+ */
+ public Covariance(RealMatrix matrix, boolean biasCorrected) {
+ 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 two columns and two rows</p>
+ *
+ * @param matrix matrix with columns representing covariates
+ * @throws IllegalArgumentException if the input matrix does not have
+ * at least two rows and two columns
+ */
+ public Covariance(RealMatrix matrix) {
+ 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 two columns and two rows)
+ * @param biasCorrected determines whether or not covariance estimates are bias-corrected
+ * @return covariance matrix
+ */
+ protected RealMatrix computeCovarianceMatrix(RealMatrix matrix, boolean biasCorrected) {
+ 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 two columns and two rows)
+ * @return covariance matrix
+ * @see #Covariance
+ */
+ protected RealMatrix computeCovarianceMatrix(RealMatrix matrix) {
+ return computeCovarianceMatrix(matrix, true);
+ }
+
+ /**
+ * Compute a covariance matrix from a rectangular array whose columns represent
+ * covariates.
+ * @param data input array (must have at least two columns and two rows)
+ * @param biasCorrected determines whether or not covariance estimates are bias-corrected
+ * @return covariance matrix
+ */
+ protected RealMatrix computeCovarianceMatrix(double[][] data, boolean biasCorrected) {
+ return computeCovarianceMatrix(new BlockRealMatrix(data), biasCorrected);
+ }
+
+ /**
+ * Create a covariance matrix from a rectangual array whose columns represent
+ * covariates. Covariances are computed using the bias-corrected formula.
+ * @param data input array (must have at least two columns and two rows)
+ * @return covariance matrix
+ * @see #Covariance
+ */
+ protected RealMatrix computeCovarianceMatrix(double[][] data) {
+ 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 IllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double covariance(final double[] xArray, final double[] yArray, boolean biasCorrected)
+ throws IllegalArgumentException {
+ Mean mean = new Mean();
+ double result = 0d;
+ int length = xArray.length;
+ if (length != yArray.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, length, yArray.length);
+ } else if (length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, 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 IllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double covariance(final double[] xArray, final double[] yArray)
+ throws IllegalArgumentException {
+ return covariance(xArray, yArray, true);
+ }
+
+ /**
+ * Throws IllegalArgumentException of the matrix does not have at least
+ * two columns and two rows
+ * @param matrix matrix to check
+ */
+ private void checkSufficientData(final RealMatrix matrix) {
+ int nRows = matrix.getRowDimension();
+ int nCols = matrix.getColumnDimension();
+ if (nRows < 2 || nCols < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_ROWS_AND_COLUMNS,
+ nRows, nCols);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/correlation/PearsonsCorrelation.java b/src/main/java/org/apache/commons/math/stat/correlation/PearsonsCorrelation.java
new file mode 100644
index 0000000..6467c69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/correlation/PearsonsCorrelation.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.math.stat.correlation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.distribution.TDistribution;
+import org.apache.commons.math.distribution.TDistributionImpl;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.stat.regression.SimpleRegression;
+import org.apache.commons.math.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>
+ * <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.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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.
+ *
+ * @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 PearsonsCorrelation(double[][] data) {
+ this(new BlockRealMatrix(data));
+ }
+
+ /**
+ * Create a PearsonsCorrelation from a RealMatrix whose columns
+ * represent variables to be correlated.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ */
+ public PearsonsCorrelation(RealMatrix matrix) {
+ checkSufficientData(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
+ *
+ * @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>
+ *
+ * @return matrix of correlation standard errors
+ */
+ 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>
+ *
+ * @return matrix of p-values
+ * @throws MathException if an error occurs estimating probabilities
+ */
+ public RealMatrix getCorrelationPValues() throws MathException {
+ TDistribution tDistribution = new TDistributionImpl(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.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(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 correlation matrix for the columns of the
+ * input rectangular array. The colums of the array represent values
+ * of variables to be correlated.
+ *
+ * @param data matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(double[][] data) {
+ return computeCorrelationMatrix(new BlockRealMatrix(data));
+ }
+
+ /**
+ * Computes the Pearson's product-moment correlation coefficient between the two arrays.
+ *
+ * </p>Throws IllegalArgumentException if the arrays do not have the same length
+ * or their common length is less than 2</p>
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return Returns Pearson's correlation coefficient for the two arrays
+ * @throws IllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double correlation(final double[] xArray, final double[] yArray) throws IllegalArgumentException {
+ SimpleRegression regression = new SimpleRegression();
+ if (xArray.length != yArray.length) {
+ throw new DimensionMismatchException(xArray.length, yArray.length);
+ } else if (xArray.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 IllegalArgumentException of the matrix does not have at least
+ * two columns and two rows
+ *
+ * @param matrix matrix to check for sufficiency
+ */
+ private void checkSufficientData(final RealMatrix matrix) {
+ int nRows = matrix.getRowDimension();
+ int nCols = matrix.getColumnDimension();
+ if (nRows < 2 || nCols < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_ROWS_AND_COLUMNS,
+ nRows, nCols);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/correlation/SpearmansCorrelation.java b/src/main/java/org/apache/commons/math/stat/correlation/SpearmansCorrelation.java
new file mode 100644
index 0000000..fe121fe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/correlation/SpearmansCorrelation.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.math.stat.correlation;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.stat.ranking.NaturalRanking;
+import org.apache.commons.math.stat.ranking.RankingAlgorithm;
+
+/**
+ * <p>Spearman's rank correlation. This implementation performs a rank
+ * transformation on the input data and then computes {@link PearsonsCorrelation}
+ * on the ranked data.</p>
+ *
+ * <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.</p>
+ *
+ * @since 2.0
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+
+public class SpearmansCorrelation {
+
+ /** Input data */
+ private final RealMatrix data;
+
+ /** Ranking algorithm */
+ private final RankingAlgorithm rankingAlgorithm;
+
+ /** Rank correlation */
+ private final PearsonsCorrelation rankCorrelation;
+
+ /**
+ * Create a SpearmansCorrelation with the given input data matrix
+ * and ranking algorithm.
+ *
+ * @param dataMatrix matrix of data with columns representing
+ * variables to correlate
+ * @param rankingAlgorithm ranking algorithm
+ */
+ public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm) {
+ this.data = dataMatrix.copy();
+ this.rankingAlgorithm = rankingAlgorithm;
+ rankTransform(data);
+ rankCorrelation = new PearsonsCorrelation(data);
+ }
+
+ /**
+ * 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 without data.
+ */
+ public SpearmansCorrelation() {
+ data = null;
+ this.rankingAlgorithm = new NaturalRanking();
+ rankCorrelation = null;
+ }
+
+ /**
+ * Calculate the Spearman Rank Correlation Matrix.
+ *
+ * @return Spearman Rank Correlation Matrix
+ */
+ 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>
+ *
+ * @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(RealMatrix matrix) {
+ RealMatrix matrixCopy = matrix.copy();
+ rankTransform(matrixCopy);
+ 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(double[][] matrix) {
+ return computeCorrelationMatrix(new BlockRealMatrix(matrix));
+ }
+
+ /**
+ * Computes the Spearman's rank correlation coefficient between the two arrays.
+ *
+ * </p>Throws IllegalArgumentException if the arrays do not have the same length
+ * or their common length is less than 2</p>
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return Returns Spearman's rank correlation coefficient for the two arrays
+ * @throws IllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double correlation(final double[] xArray, final double[] yArray)
+ throws IllegalArgumentException {
+ if (xArray.length != yArray.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, xArray.length, yArray.length);
+ } else if (xArray.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, xArray.length, 2);
+ } else {
+ return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(xArray),
+ rankingAlgorithm.rank(yArray));
+ }
+ }
+
+ /**
+ * Applies rank transform to each of the columns of <code>matrix</code>
+ * using the current <code>rankingAlgorithm</code>
+ *
+ * @param matrix matrix to transform
+ */
+ private void rankTransform(RealMatrix matrix) {
+ for (int i = 0; i < matrix.getColumnDimension(); i++) {
+ matrix.setColumn(i, rankingAlgorithm.rank(matrix.getColumn(i)));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/correlation/package.html b/src/main/java/org/apache/commons/math/stat/correlation/package.html
new file mode 100644
index 0000000..8b12fc2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/correlation/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 744716 $ $Date: 2009-02-15 19:38:49 +0100 (dim. 15 févr. 2009) $ -->
+ <body>
+ Correlations/Covariance computations.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java b/src/main/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.java
new file mode 100644
index 0000000..9e721ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.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.math.stat.descriptive;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ *
+ * 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>
+ *
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+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, an IllegalArgumentException is thrown.</p>
+ * @param values input array
+ * @return the value of the statistic applied to the input array
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[])
+ */
+ @Override
+ public double evaluate(final double[] values) {
+ 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
+ * IllegalArgumentException 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
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatistic#evaluate(double[], int, int)
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length) {
+ 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 IllegalArgumentException if values is null
+ * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#incrementAll(double[])
+ */
+ public void incrementAll(double[] values) {
+ 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 IllegalArgumentException if values is null
+ * @see org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic#incrementAll(double[], int, int)
+ */
+ public void incrementAll(double[] values, int begin, int length) {
+ 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 MathUtils.equalsIncludingNaN(stat.getResult(), this.getResult()) &&
+ MathUtils.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/math/stat/descriptive/AbstractUnivariateStatistic.java b/src/main/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.java
new file mode 100644
index 0000000..354dee6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.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.math.stat.descriptive;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NotPositiveException;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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.
+ * @param values data array to store
+ * @param begin the index of the first element to include
+ * @param length the number of elements to include
+ * @see #evaluate()
+ */
+ public void setData(final double[] values, final int begin, final int length) {
+ 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
+ * </p>
+ * @return the value of the statistic applied to the stored data
+ */
+ public double evaluate() {
+ return evaluate(storedData);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double evaluate(final double[] values) {
+ test(values, 0, 0);
+ return evaluate(values, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract double evaluate(final double[] values, final int begin, final int length);
+
+ /**
+ * {@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>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.
+ * </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 IllegalArgumentException if the indices are invalid or the array is null
+ */
+ protected boolean test(
+ final double[] values,
+ final int begin,
+ final int length) {
+
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END);
+ }
+
+ if (length == 0) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * 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 IllegalArgumentException 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) {
+
+ if (weights == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ if (weights.length != values.length) {
+ throw new DimensionMismatchException(weights.length, values.length);
+ }
+
+ boolean containsPositiveWeight = false;
+ for (int i = begin; i < begin + length; i++) {
+ if (Double.isNaN(weights[i])) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NAN_ELEMENT_AT_INDEX, i);
+ }
+ if (Double.isInfinite(weights[i])) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INFINITE_ARRAY_ELEMENT, weights[i], i);
+ }
+ if (weights[i] < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, i, weights[i]);
+ }
+ if (!containsPositiveWeight && weights[i] > 0.0) {
+ containsPositiveWeight = true;
+ }
+ }
+
+ if (!containsPositiveWeight) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
+ }
+
+ return test(values, begin, length);
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.java
new file mode 100644
index 0000000..98c58c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.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.math.stat.descriptive;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * <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 unecessary computation and synchronization delays.</p>
+ *
+ * @since 2.0
+ * @version $Revision: 811833 $ $Date: 2009-09-06 18:27:50 +0200 (dim. 06 sept. 2009) $
+ *
+ */
+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() {
+ 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.
+ * @see #createContributingStatistics()
+ */
+ public AggregateSummaryStatistics(SummaryStatistics prototypeStatistics) {
+ 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);
+
+ 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<SummaryStatistics> statistics) {
+ if (statistics == null) {
+ return null;
+ }
+ Iterator<SummaryStatistics> iterator = statistics.iterator();
+ if (!iterator.hasNext()) {
+ return null;
+ }
+ SummaryStatistics current = iterator.next();
+ long n = current.getN();
+ double min = current.getMin();
+ double sum = current.getSum();
+ double max = current.getMax();
+ double m2 = current.getSecondMoment();
+ 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;
+ m2 = m2 + current.getSecondMoment() + 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
+ */
+ public 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/math/stat/descriptive/DescriptiveStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java
new file mode 100644
index 0000000..e5a18dc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.java
@@ -0,0 +1,721 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math.stat.descriptive.moment.Kurtosis;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.Skewness;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+import org.apache.commons.math.stat.descriptive.rank.Max;
+import org.apache.commons.math.stat.descriptive.rank.Min;
+import org.apache.commons.math.stat.descriptive.rank.Percentile;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math.util.ResizableDoubleArray;
+import org.apache.commons.math.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>
+ *
+ * @version $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $
+ */
+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
+ */
+ protected 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.
+ */
+ public DescriptiveStatistics(int window) {
+ 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
+ */
+ public DescriptiveStatistics(DescriptiveStatistics original) {
+ 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.
+ */
+ public void removeMostRecentValue() {
+ eDA.discardMostRecentElements(1);
+ }
+
+ /**
+ * 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
+ */
+ public double replaceMostRecentValue(double v) {
+ 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
+ * @return The geometricMean, Double.NaN if no values have been added,
+ * or if the product of the available values is less than or equal to 0.
+ */
+ public double getGeometricMean() {
+ return apply(geometricMeanImpl);
+ }
+
+ /**
+ * 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.
+ */
+ public double getVariance() {
+ return apply(varianceImpl);
+ }
+
+ /**
+ * 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 skewness of the available values. Skewness is a
+ * measure of the asymmetry of a given distribution.
+ * @return The skewness, Double.NaN if no values have been added
+ * or 0.0 for a value set &lt;=2.
+ */
+ 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 no values have been added, or 0.0
+ * for a value set &lt;=3.
+ */
+ 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 which 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
+ * @param windowSize sets the size of the window.
+ */
+ public void setWindowSize(int windowSize) {
+ if (windowSize < 1) {
+ if (windowSize != INFINITE_WINDOW) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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>IllegalArgumentException</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 IllegalStateException if percentile implementation has been
+ * overridden and the supplied implementation does not support setQuantile
+ * values
+ */
+ public double getPercentile(double p) {
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
+ percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
+ } catch (IllegalAccessException e2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
+ SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
+ } catch (InvocationTargetException e3) {
+ throw MathRuntimeException.createIllegalArgumentException(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);
+ outBuffer.append("median: ").append(getPercentile(50)).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) {
+ return stat.evaluate(eDA.getInternalValues(), eDA.start(), eDA.getNumElements());
+ }
+
+ // 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 IllegalArgumentException if the supplied implementation does not
+ * provide a <code>setQuantile</code> method
+ * @since 1.2
+ */
+ public synchronized void setPercentileImpl(
+ UnivariateStatistic percentileImpl) {
+ try {
+ percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
+ new Class[] {Double.TYPE}).invoke(percentileImpl,
+ new Object[] {Double.valueOf(50.0d)});
+ } catch (NoSuchMethodException e1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
+ percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
+ } catch (IllegalAccessException e2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
+ SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
+ } catch (InvocationTargetException e3) {
+ throw MathRuntimeException.createIllegalArgumentException(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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(DescriptiveStatistics source, DescriptiveStatistics 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/math/stat/descriptive/MultivariateSummaryStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.java
new file mode 100644
index 0000000..8062f5b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatistics.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.math.stat.descriptive;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.VectorialCovariance;
+import org.apache.commons.math.stat.descriptive.rank.Max;
+import org.apache.commons.math.stat.descriptive.rank.Min;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.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
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+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 MathUtils.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
+ MathUtils.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ MathUtils.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ MathUtils.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ MathUtils.equalsIncludingNaN(stat.getN(), getN()) &&
+ MathUtils.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ MathUtils.equalsIncludingNaN(stat.getSumSq(), getSumSq()) &&
+ MathUtils.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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ private void setImpl(StorelessUnivariateStatistic[] newImpl,
+ StorelessUnivariateStatistic[] oldImpl)
+ throws DimensionMismatchException, IllegalStateException {
+ 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumImpl(StorelessUnivariateStatistic[] sumImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMinImpl(StorelessUnivariateStatistic[] minImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMaxImpl(StorelessUnivariateStatistic[] maxImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl)
+ throws 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMeanImpl(StorelessUnivariateStatistic[] meanImpl)
+ throws DimensionMismatchException {
+ setImpl(meanImpl, this.meanImpl);
+ }
+
+ /**
+ * Throws IllegalStateException if n > 0.
+ */
+ private void checkEmpty() {
+ if (n > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ 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/math/stat/descriptive/StatisticalMultivariateSummary.java b/src/main/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.java
new file mode 100644
index 0000000..517788c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/StatisticalMultivariateSummary.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.math.stat.descriptive;
+
+import org.apache.commons.math.linear.RealMatrix;
+
+/**
+ * Reporting interface for basic multivariate statistics.
+ *
+ * @since 1.2
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+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/math/stat/descriptive/StatisticalSummary.java b/src/main/java/org/apache/commons/math/stat/descriptive/StatisticalSummary.java
new file mode 100644
index 0000000..5592053
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/StatisticalSummary.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.math.stat.descriptive;
+
+/**
+ * Reporting interface for basic univariate statistics.
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+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/math/stat/descriptive/StatisticalSummaryValues.java b/src/main/java/org/apache/commons/math/stat/descriptive/StatisticalSummaryValues.java
new file mode 100644
index 0000000..e72639a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/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.math.stat.descriptive;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Value object representing the results of a univariate statistical summary.
+ *
+ * @version $Revision: 1054186 $ $Date: 2011-01-01 03:28:46 +0100 (sam. 01 janv. 2011) $
+ */
+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 MathUtils.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ MathUtils.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ MathUtils.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ MathUtils.equalsIncludingNaN(stat.getN(), getN()) &&
+ MathUtils.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ MathUtils.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() {
+ StringBuilder outBuffer = new StringBuilder();
+ 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/math/stat/descriptive/StorelessUnivariateStatistic.java b/src/main/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.java
new file mode 100644
index 0000000..9b9fcb4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.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.math.stat.descriptive;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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 IllegalArgumentException if the array is null
+ */
+ void incrementAll(double[] values);
+
+ /**
+ * 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 IllegalArgumentException if the array is null or the index
+ */
+ void incrementAll(double[] values, int start, int length);
+
+ /**
+ * 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/math/stat/descriptive/SummaryStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java
new file mode 100644
index 0000000..017a84d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/SummaryStatistics.java
@@ -0,0 +1,717 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.SecondMoment;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+import org.apache.commons.math.stat.descriptive.rank.Max;
+import org.apache.commons.math.stat.descriptive.rank.Min;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math.util.MathUtils;
+import org.apache.commons.math.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>
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+public class SummaryStatistics implements StatisticalSummary, Serializable {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = -2021321786743555871L;
+
+ /** count of values that have been added */
+ protected long n = 0;
+
+ /** SecondMoment is used to compute the mean and variance */
+ protected SecondMoment secondMoment = new SecondMoment();
+
+ /** sum of values that have been added */
+ protected Sum sum = new Sum();
+
+ /** sum of the square of each value that has been added */
+ protected SumOfSquares sumsq = new SumOfSquares();
+
+ /** min of values that have been added */
+ protected Min min = new Min();
+
+ /** max of values that have been added */
+ protected Max max = new Max();
+
+ /** sumLog of values that have been added */
+ protected SumOfLogs sumLog = new SumOfLogs();
+
+ /** geoMean of values that have been added */
+ protected GeometricMean geoMean = new GeometricMean(sumLog);
+
+ /** mean of values that have been added */
+ protected Mean mean = new Mean();
+
+ /** variance of values that have been added */
+ protected Variance variance = new Variance();
+
+ /** 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
+ */
+ public SummaryStatistics(SummaryStatistics original) {
+ 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 instanceof Mean)) {
+ meanImpl.increment(value);
+ }
+ if (!(varianceImpl instanceof Variance)) {
+ varianceImpl.increment(value);
+ }
+ if (!(geoMeanImpl instanceof GeometricMean)) {
+ 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() {
+ if (mean == meanImpl) {
+ return new Mean(secondMoment).getResult();
+ } else {
+ 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 variance of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the variance
+ */
+ public double getVariance() {
+ if (varianceImpl == variance) {
+ return new Variance(secondMoment).getResult();
+ } else {
+ return varianceImpl.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("mean: ").append(getMean()).append(endl);
+ outBuffer.append("geometric mean: ").append(getGeometricMean())
+ .append(endl);
+ outBuffer.append("variance: ").append(getVariance()).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 MathUtils.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
+ MathUtils.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ MathUtils.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ MathUtils.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ MathUtils.equalsIncludingNaN(stat.getN(), getN()) &&
+ MathUtils.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ MathUtils.equalsIncludingNaN(stat.getSumsq(), getSumsq()) &&
+ MathUtils.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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setSumImpl(StorelessUnivariateStatistic sumImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setMinImpl(StorelessUnivariateStatistic minImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setMaxImpl(StorelessUnivariateStatistic maxImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl) {
+ 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 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 IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setMeanImpl(StorelessUnivariateStatistic meanImpl) {
+ 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 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 varianceImpl the StorelessUnivariateStatistic instance to use for
+ * computing the variance
+ * @throws IllegalStateException if data has already been added (i.e if n >
+ * 0)
+ * @since 1.2
+ */
+ public void setVarianceImpl(StorelessUnivariateStatistic varianceImpl) {
+ checkEmpty();
+ this.varianceImpl = varianceImpl;
+ }
+
+ /**
+ * Throws IllegalStateException if n > 0.
+ */
+ private void checkEmpty() {
+ if (n > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SummaryStatistics source, SummaryStatistics dest) {
+ 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.sumLogImpl = source.sumLogImpl.copy();
+ dest.sumsqImpl = source.sumsqImpl.copy();
+ if (source.getGeoMeanImpl() instanceof GeometricMean) {
+ // Keep geoMeanImpl, sumLogImpl in synch
+ dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl);
+ } else {
+ dest.geoMeanImpl = source.geoMeanImpl.copy();
+ }
+ SecondMoment.copy(source.secondMoment, dest.secondMoment);
+ dest.n = source.n;
+
+ // 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/math/stat/descriptive/SynchronizedDescriptiveStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.java
new file mode 100644
index 0000000..f1a932d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.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.math.stat.descriptive;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math.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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an instance with infinite window
+ */
+ public SynchronizedDescriptiveStatistics() {
+ this(INFINITE_WINDOW);
+ }
+
+ /**
+ * Construct an instance with finite window
+ * @param window the finite window size.
+ */
+ public SynchronizedDescriptiveStatistics(int window) {
+ super(window);
+ }
+
+ /**
+ * A copy constructor. Creates a deep-copy of the {@code original}.
+ *
+ * @param original the {@code SynchronizedDescriptiveStatistics} instance to copy
+ */
+ public SynchronizedDescriptiveStatistics(SynchronizedDescriptiveStatistics original) {
+ 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[] getValues() {
+ return super.getValues();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int getWindowSize() {
+ return super.getWindowSize();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setWindowSize(int windowSize) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SynchronizedDescriptiveStatistics source,
+ SynchronizedDescriptiveStatistics dest) {
+ synchronized (source) {
+ synchronized (dest) {
+ DescriptiveStatistics.copy(source, dest);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java
new file mode 100644
index 0000000..190e092
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatistics.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.math.stat.descriptive;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.linear.RealMatrix;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math.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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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 {
+ super.setSumImpl(sumImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getSumsqImpl() {
+ return super.getSumsqImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl)
+ throws DimensionMismatchException {
+ super.setSumsqImpl(sumsqImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMinImpl() {
+ return super.getMinImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMinImpl(StorelessUnivariateStatistic[] minImpl)
+ throws DimensionMismatchException {
+ super.setMinImpl(minImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMaxImpl() {
+ return super.getMaxImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMaxImpl(StorelessUnivariateStatistic[] maxImpl)
+ throws DimensionMismatchException {
+ super.setMaxImpl(maxImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getSumLogImpl() {
+ return super.getSumLogImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl)
+ throws DimensionMismatchException {
+ super.setSumLogImpl(sumLogImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getGeoMeanImpl() {
+ return super.getGeoMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl)
+ throws DimensionMismatchException {
+ super.setGeoMeanImpl(geoMeanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMeanImpl() {
+ return super.getMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMeanImpl(StorelessUnivariateStatistic[] meanImpl)
+ throws DimensionMismatchException {
+ super.setMeanImpl(meanImpl);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java
new file mode 100644
index 0000000..07bfbf2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math.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
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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
+ */
+ public SynchronizedSummaryStatistics(SynchronizedSummaryStatistics original) {
+ 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 getVariance() {
+ return super.getVariance();
+ }
+
+ /**
+ * {@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) {
+ super.setSumImpl(sumImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getSumsqImpl() {
+ return super.getSumsqImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl) {
+ super.setSumsqImpl(sumsqImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMinImpl() {
+ return super.getMinImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMinImpl(StorelessUnivariateStatistic minImpl) {
+ super.setMinImpl(minImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMaxImpl() {
+ return super.getMaxImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMaxImpl(StorelessUnivariateStatistic maxImpl) {
+ super.setMaxImpl(maxImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getSumLogImpl() {
+ return super.getSumLogImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl) {
+ super.setSumLogImpl(sumLogImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getGeoMeanImpl() {
+ return super.getGeoMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl) {
+ super.setGeoMeanImpl(geoMeanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMeanImpl() {
+ return super.getMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMeanImpl(StorelessUnivariateStatistic meanImpl) {
+ super.setMeanImpl(meanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getVarianceImpl() {
+ return super.getVarianceImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setVarianceImpl(StorelessUnivariateStatistic varianceImpl) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SynchronizedSummaryStatistics source,
+ SynchronizedSummaryStatistics dest) {
+ synchronized (source) {
+ synchronized (dest) {
+ SummaryStatistics.copy(source, dest);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java b/src/main/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.java
new file mode 100644
index 0000000..92c9ee2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/UnivariateStatistic.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.math.stat.descriptive;
+
+
+/**
+ * Base interface implemented by all statistics.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public interface UnivariateStatistic {
+
+ /**
+ * 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
+ */
+ double evaluate(double[] values);
+
+ /**
+ * 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
+ */
+ double evaluate(double[] values, int begin, int length);
+
+ /**
+ * 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/math/stat/descriptive/WeightedEvaluation.java b/src/main/java/org/apache/commons/math/stat/descriptive/WeightedEvaluation.java
new file mode 100644
index 0000000..54a0216
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/WeightedEvaluation.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.math.stat.descriptive;
+
+/**
+ * Weighted evaluation for statistics.
+ *
+ * @since 2.1
+ * @version $Revision: 894474 $ $Date: 2009-12-29 21:02:37 +0100 (mar. 29 déc. 2009) $
+ */
+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
+ */
+ double evaluate(double[] values, double[] weights);
+
+ /**
+ * 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
+ */
+ double evaluate(double[] values, double[] weights, int begin, int length);
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.java
new file mode 100644
index 0000000..4103e50
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/FirstMoment.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+/**
+ * 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.</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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+public 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
+ */
+ public 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
+ */
+ public FirstMoment(FirstMoment original) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(FirstMoment source, FirstMoment 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/math/stat/descriptive/moment/FourthMoment.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.java
new file mode 100644
index 0000000..6e7d8d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/FourthMoment.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+/**
+ * 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. </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>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class FourthMoment extends ThirdMoment implements Serializable{
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4763990447117157611L;
+
+ /** fourth moment of values that have been added */
+ protected double m4;
+
+ /**
+ * Create a FourthMoment instance
+ */
+ public 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
+ */
+ public FourthMoment(FourthMoment original) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(FourthMoment source, FourthMoment dest) {
+ ThirdMoment.copy(source, dest);
+ dest.m4 = source.m4;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.java
new file mode 100644
index 0000000..a24a3c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/GeometricMean.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public GeometricMean(GeometricMean original) {
+ 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();
+ 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 IllegalArgumentException 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) {
+ 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 IllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumLogImpl(
+ StorelessUnivariateStatistic sumLogImpl) {
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(GeometricMean source, GeometricMean dest) {
+ dest.setData(source.getDataRef());
+ dest.sumOfLogs = source.sumOfLogs.copy();
+ }
+
+
+ /**
+ * Throws IllegalStateException if n > 0.
+ */
+ private void checkEmpty() {
+ if (getN() > 0) {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
+ getN());
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.java
new file mode 100644
index 0000000..f648051
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Kurtosis.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+
+/**
+ * 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.</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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public Kurtosis(Kurtosis original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (incMoment) {
+ moment.increment(d);
+ } else {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.CANNOT_INCREMENT_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS);
+ }
+ }
+
+ /**
+ * {@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.m4 -
+ 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();
+ } else {
+ throw MathRuntimeException.createIllegalStateException(
+ LocalizedFormats.CANNOT_CLEAR_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS);
+ }
+ }
+
+ /**
+ * {@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 IllegalArgumentException 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) {
+ // 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Kurtosis source, Kurtosis dest) {
+ dest.setData(source.getDataRef());
+ dest.moment = source.moment.copy();
+ dest.incMoment = source.incMoment;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/Mean.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Mean.java
new file mode 100644
index 0000000..c5aa9da
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Mean.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+
+/**
+ * <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.
+ * </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.
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public Mean(Mean original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @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 IllegalArgumentException 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) {
+ 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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) {
+ 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>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>
+ * </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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights) {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Mean copy() {
+ Mean result = new Mean();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Mean source, Mean dest) {
+ dest.setData(source.getDataRef());
+ dest.incMoment = source.incMoment;
+ dest.moment = source.moment.copy();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.java
new file mode 100644
index 0000000..ae8ef8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/SecondMoment.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+/**
+ * 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.</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>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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
+ */
+ public SecondMoment(SecondMoment original) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SecondMoment source, SecondMoment dest) {
+ FirstMoment.copy(source, dest);
+ dest.m2 = source.m2;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/SemiVariance.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/SemiVariance.java
new file mode 100644
index 0000000..04aa456
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/SemiVariance.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
+
+/**
+ * <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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ * @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
+ */
+ public SemiVariance(final SemiVariance original) {
+ copy(original, this);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SemiVariance copy() {
+ SemiVariance result = new SemiVariance();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(final SemiVariance source, SemiVariance dest) {
+ dest.setData(source.getDataRef());
+ dest.biasCorrected = source.biasCorrected;
+ dest.varianceDirection = source.varianceDirection;
+ }
+
+
+ /**
+ * This method calculates {@link SemiVariance} for the entire array against the mean, using
+ * instance properties varianceDirection and biasCorrection.
+ *
+ * @param values the input array
+ * @return the SemiVariance
+ * @throws IllegalArgumentException if values is null
+ *
+ */
+ @Override
+ public double evaluate(final double[] values) {
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ return evaluate(values, 0, values.length);
+ }
+
+
+ /**
+ * <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 IllegalArgumentException if the parameters are not valid
+ *
+ */
+ @Override
+ public double evaluate(final double[] values, final int start, final int length) {
+ 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 IllegalArgumentException if values is null
+ *
+ */
+ public double evaluate(final double[] values, Direction direction) {
+ 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>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param cutoff the reference point
+ * @return the SemiVariance
+ * @throws IllegalArgumentException if values is null
+ */
+ public double evaluate(final double[] values, final double cutoff) {
+ 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>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
+ * @return the SemiVariance
+ * @throws IllegalArgumentException if values is null
+ */
+ public double evaluate(final double[] values, final double cutoff, final Direction direction) {
+ 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 IllegalArgumentException 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) {
+
+ 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/math/stat/descriptive/moment/Skewness.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java
new file mode 100644
index 0000000..d16f956
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Skewness.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ * <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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public Skewness(Skewness original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @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 IllegalArgumentException 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) {
+
+ // 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Skewness source, Skewness 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/math/stat/descriptive/moment/StandardDeviation.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java
new file mode 100644
index 0000000..837ae3b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviation.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public StandardDeviation(StandardDeviation original) {
+ 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>IllegalArgumentException</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 IllegalArgumentException if the array is null
+ */
+ @Override
+ public double evaluate(final double[] values) {
+ 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>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 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 IllegalArgumentException 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) {
+ 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 IllegalArgumentException 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) {
+ 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>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
+ * @return the standard deviation of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException if the array is null
+ */
+ public double evaluate(final double[] values, final double mean) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(StandardDeviation source, StandardDeviation dest) {
+ dest.setData(source.getDataRef());
+ dest.variance = source.variance.copy();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.java
new file mode 100644
index 0000000..5c50989
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/ThirdMoment.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+
+/**
+ * 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.</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>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public 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
+ */
+ public 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
+ */
+ public ThirdMoment(ThirdMoment original) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(ThirdMoment source, ThirdMoment dest) {
+ SecondMoment.copy(source, dest);
+ dest.m3 = source.m3;
+ dest.nDevSq = source.nDevSq;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/moment/Variance.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Variance.java
new file mode 100644
index 0000000..6ce6835
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/Variance.java
@@ -0,0 +1,610 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.exception.NullArgumentException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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;
+
+ /**
+ * Boolean test to determine if this Variance should also increment
+ * the second moment, this evaluates to false when this Variance is
+ * constructed with an external SecondMoment as a parameter.
+ */
+ protected boolean incMoment = true;
+
+ /**
+ * Determines whether or not bias correction is applied when computing the
+ * value of the statisic. 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.
+ *
+ * @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
+ */
+ public Variance(Variance original) {
+ 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>
+ */
+ @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>IllegalArgumentException</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 IllegalArgumentException if the array is null
+ */
+ @Override
+ public double evaluate(final double[] values) {
+ 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.
+ * <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>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 variance of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+
+ 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, MathUtils.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>IllegalArgumentException</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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) {
+
+ 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, MathUtils.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>
+ * </ul></p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights) {
+ 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>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 variance of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+
+ 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>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
+ * @return the variance of the values or Double.NaN if the array is empty
+ * @throws IllegalArgumentException if the array is null
+ */
+ public double evaluate(final double[] values, final double mean) {
+ 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, MathUtils.normalizeArray(weights, values.length), mean); </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>
+ *
+ * @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 IllegalArgumentException 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) {
+
+ 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 = 0; i < weights.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, MathUtils.normalizeArray(weights, values.length), mean); </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>
+ * </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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights, final double mean) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Variance source, Variance dest) {
+ if (source == null ||
+ dest == null) {
+ throw new NullArgumentException();
+ }
+ 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/math/stat/descriptive/moment/VectorialCovariance.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.java
new file mode 100644
index 0000000..71afc68
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovariance.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+
+/**
+ * Returns the covariance matrix of the available vectors.
+ * @since 1.2
+ * @version $Revision: 922714 $ $Date: 2010-03-14 02:35:14 +0100 (dim. 14 mars 2010) $
+ */
+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
+ * @exception 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/math/stat/descriptive/moment/VectorialMean.java b/src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialMean.java
new file mode 100644
index 0000000..ef57657
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/VectorialMean.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.math.stat.descriptive.moment;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.DimensionMismatchException;
+
+/**
+ * Returns the arithmetic mean of the available vectors.
+ * @since 1.2
+ * @version $Revision: 922714 $ $Date: 2010-03-14 02:35:14 +0100 (dim. 14 mars 2010) $
+ */
+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
+ * @exception 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/math/stat/descriptive/moment/package.html b/src/main/java/org/apache/commons/math/stat/descriptive/moment/package.html
new file mode 100644
index 0000000..e024095
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/moment/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Summary statistics based on moments.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/package.html b/src/main/java/org/apache/commons/math/stat/descriptive/package.html
new file mode 100644
index 0000000..981fda4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/package.html
@@ -0,0 +1,41 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ -->
+ <body>
+ Generic univariate summary statistic objects.
+
+ <h3>UnivariateStatistic API Usage Examples:</h3>
+ <h4>UnivariateStatistic:</h4>
+ <code>/* evaluation approach */<br/> double[] values = new double[] { 1, 2,
+ 3, 4, 5 };<br/> <span style="font-weight: bold;">UnivariateStatistic stat
+ = new Mean();</span><br/> System.out.println("mean = " + <span
+ style="font-weight: bold;">stat.evaluate(values)</span>);<br/> </code>
+ <h4>StorelessUnivariateStatistic:</h4>
+ <code>/* incremental approach */<br/> double[] values = new double[] { 1, 2,
+ 3, 4, 5 };<br/> <span style="font-weight: bold;">
+ StorelessUnivariateStatistic stat = new Mean();</span><br/>
+ System.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;
+ System.out.println("current mean = " + <span style="font-weight: bold;">
+ stat2.getResult()</span>);<br/> }<br/> <span style="font-weight: bold;">
+ stat.clear();</span><br/> System.out.println("mean after clear is NaN = "
+ + <span style="font-weight: bold;">stat.getResult()</span>);</code>
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/rank/Max.java b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Max.java
new file mode 100644
index 0000000..1b15750
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Max.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public Max(Max original) {
+ 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>IllegalArgumentException</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 IllegalArgumentException 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) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Max source, Max dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/rank/Median.java b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Median.java
new file mode 100644
index 0000000..6e13b13
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Median.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.math.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+
+/**
+ * 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>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class Median extends Percentile implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3961477041290915687L;
+
+ /**
+ * Default constructor.
+ */
+ public Median() {
+ super(50.0);
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Median} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Median} instance to copy
+ */
+ public Median(Median original) {
+ super(original);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/rank/Min.java b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Min.java
new file mode 100644
index 0000000..1c264c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Min.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+/**
+ * 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public Min(Min original) {
+ 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>IllegalArgumentException</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 IllegalArgumentException 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) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Min source, Min dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java
new file mode 100644
index 0000000..0c8a90f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/rank/Percentile.java
@@ -0,0 +1,497 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.rank;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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>). If <code>pos >= n</code> return the largest
+ * element in the array; otherwise</li>
+ * <li>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>
+ * Since 2.2, Percentile implementation uses only selection instead of complete
+ * sorting and caches selection algorithm state between calls to the various
+ * {@code evaluate} methods when several percentiles are to be computed on the same data.
+ * This greatly improves efficiency, both for single percentile and multiple
+ * percentiles computations. However, it also induces a need to be sure the data
+ * at one call to {@code evaluate} is the same as the data with the cached algorithm
+ * state from the previous calls. Percentile does this by checking the array reference
+ * itself and a checksum of its content by default. If the user already knows he calls
+ * {@code evaluate} on an immutable array, he can save the checking time by calling the
+ * {@code evaluate} methods that do <em>not</em>
+ * </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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+public class Percentile extends AbstractUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8091216485095130416L;
+
+ /** Minimum size under which we use a simple insertion sort rather than Hoare's select. */
+ private static final int MIN_SELECT_SIZE = 15;
+
+ /** Maximum number of partitioning pivots cached (each level double the number of pivots). */
+ private static final int MAX_CACHED_LEVELS = 10;
+
+ /** Determines what percentile is computed when evaluate() is activated
+ * with no quantile argument */
+ private double quantile = 0.0;
+
+ /** Cached pivots. */
+ private int[] cachedPivots;
+
+ /**
+ * Constructs a Percentile with a default quantile
+ * value of 50.0.
+ */
+ public Percentile() {
+ this(50.0);
+ }
+
+ /**
+ * Constructs a Percentile with the specific quantile value.
+ * @param p the quantile
+ * @throws IllegalArgumentException if p is not greater than 0 and less
+ * than or equal to 100
+ */
+ public Percentile(final double p) {
+ setQuantile(p);
+ cachedPivots = null;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Percentile} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Percentile} instance to copy
+ */
+ public Percentile(Percentile original) {
+ copy(original, this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setData(final double[] values) {
+ if (values == null) {
+ cachedPivots = null;
+ } else {
+ cachedPivots = new int[(0x1 << MAX_CACHED_LEVELS) - 1];
+ Arrays.fill(cachedPivots, -1);
+ }
+ super.setData(values);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setData(final double[] values, final int begin, final int length) {
+ if (values == null) {
+ cachedPivots = null;
+ } else {
+ cachedPivots = new int[(0x1 << MAX_CACHED_LEVELS) - 1];
+ 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
+ * </p>
+ * @param p the percentile value to compute
+ * @return the value of the statistic applied to the stored data
+ */
+ public double evaluate(final double p) {
+ 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>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) </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 IllegalArgumentException if <code>values</code> is null
+ * or p is invalid
+ */
+ public double evaluate(final double[] values, final double p) {
+ 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>IllegalArgumentException</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 IllegalArgumentException if the parameters are not valid
+ *
+ */
+ @Override
+ public double evaluate( final double[] values, final int start, final int length) {
+ 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>IllegalArgumentException</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 IllegalArgumentException 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) {
+
+ test(values, begin, length);
+
+ if ((p > 100) || (p <= 0)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUNDS_QUANTILE_VALUE, p);
+ }
+ if (length == 0) {
+ return Double.NaN;
+ }
+ if (length == 1) {
+ return values[begin]; // always return single value for n = 1
+ }
+ double n = length;
+ double pos = p * (n + 1) / 100;
+ double fpos = FastMath.floor(pos);
+ int intPos = (int) fpos;
+ double dif = pos - fpos;
+ double[] work;
+ int[] pivotsHeap;
+ if (values == getDataRef()) {
+ work = getDataRef();
+ pivotsHeap = cachedPivots;
+ } else {
+ work = new double[length];
+ System.arraycopy(values, begin, work, 0, length);
+ pivotsHeap = new int[(0x1 << MAX_CACHED_LEVELS) - 1];
+ Arrays.fill(pivotsHeap, -1);
+ }
+
+ if (pos < 1) {
+ return select(work, pivotsHeap, 0);
+ }
+ if (pos >= n) {
+ return select(work, pivotsHeap, length - 1);
+ }
+ double lower = select(work, pivotsHeap, intPos - 1);
+ double upper = select(work, pivotsHeap, intPos);
+ return lower + dif * (upper - lower);
+ }
+
+ /**
+ * Select the k<sup>th</sup> smallest element from work array
+ * @param work work array (will be reorganized during the call)
+ * @param pivotsHeap set of pivot index corresponding to elements that
+ * are already at their sorted location, stored as an implicit heap
+ * (i.e. a sorted binary tree stored in a flat array, where the
+ * children of a node at index n are at indices 2n+1 for the left
+ * child and 2n+2 for the right child, with 0-based indices)
+ * @param k index of the desired element
+ * @return k<sup>th</sup> smallest element
+ */
+ private double select(final double[] work, final int[] pivotsHeap, final int k) {
+
+ int begin = 0;
+ int end = work.length;
+ int node = 0;
+
+ while (end - begin > MIN_SELECT_SIZE) {
+
+ final int pivot;
+ if ((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, medianOf3(work, begin, end));
+ if (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 = Math.min(2 * node + 1, pivotsHeap.length); // the min is here to avoid integer overflow
+ } else {
+ // the element is in the right partition
+ begin = pivot + 1;
+ node = Math.min(2 * node + 2, pivotsHeap.length); // the min is here to avoid integer overflow
+ }
+
+ }
+
+ // the element is somewhere in the small sub-array
+ // sort the sub-array using insertion sort
+ insertionSort(work, begin, end);
+ return work[k];
+
+ }
+
+ /** Select a pivot index as the median of three
+ * @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
+ */
+ int medianOf3(final double[] work, final int begin, final int end) {
+
+ 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;
+ }
+ }
+
+ }
+
+ /**
+ * Partition an array slice around a pivot
+ * <p>
+ * Partitioning exchanges array elements such that all elements
+ * smaller than pivot are before it and all elements larger than
+ * pivot are after it
+ * </p>
+ * @param work data array
+ * @param begin index of the first element of the slice
+ * @param end index after the last element of the slice
+ * @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;
+
+ }
+
+ /**
+ * Sort in place a (small) array slice using insertion sort
+ * @param work array to sort
+ * @param begin index of the first element of the slice to sort
+ * @param end index after the last element of the slice to sort
+ */
+ private void insertionSort(final double[] work, final int begin, final int end) {
+ for (int j = begin + 1; j < end; j++) {
+ final double saved = work[j];
+ int i = j - 1;
+ while ((i >= begin) && (saved < work[i])) {
+ work[i + 1] = work[i];
+ i--;
+ }
+ work[i + 1] = saved;
+ }
+ }
+
+ /**
+ * Returns the value of the quantile field (determines what percentile is
+ * computed when evaluate() is called with no quantile argument).
+ *
+ * @return quantile
+ */
+ 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 IllegalArgumentException if p is not greater than 0 and less
+ * than or equal to 100
+ */
+ public void setQuantile(final double p) {
+ if (p <= 0 || p > 100) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUNDS_QUANTILE_VALUE, p);
+ }
+ quantile = p;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Percentile copy() {
+ Percentile result = new Percentile();
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Percentile to copy
+ * @param dest Percentile to copy to
+ * @throws NullPointerException if either source or dest is null
+ */
+ public static void copy(Percentile source, Percentile dest) {
+ dest.setData(source.getDataRef());
+ if (source.cachedPivots != null) {
+ System.arraycopy(source.cachedPivots, 0, dest.cachedPivots, 0, source.cachedPivots.length);
+ }
+ dest.quantile = source.quantile;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/rank/package.html b/src/main/java/org/apache/commons/math/stat/descriptive/rank/package.html
new file mode 100644
index 0000000..c69107b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/rank/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Summary statistics based on ranks.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/summary/Product.java b/src/main/java/org/apache/commons/math/stat/descriptive/summary/Product.java
new file mode 100644
index 0000000..c7d1d76
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/summary/Product.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.math.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Returns the product of the available values.
+ * <p>
+ * If there are no values in the dataset, or 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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 = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Product} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Product} instance to copy
+ */
+ public Product(Product original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n == 0) {
+ value = d;
+ } else {
+ value *= d;
+ }
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = Double.NaN;
+ 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>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 product of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ double product = Double.NaN;
+ if (test(values, begin, length)) {
+ 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>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>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 Double.NaN if length = 0
+ * @throws IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) {
+ double product = Double.NaN;
+ if (test(values, weights, begin, length)) {
+ 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>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>
+ * </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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights) {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Product copy() {
+ Product result = new Product();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Product source, Product dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/summary/Sum.java b/src/main/java/org/apache/commons/math/stat/descriptive/summary/Sum.java
new file mode 100644
index 0000000..7188ea8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/summary/Sum.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.math.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+
+/**
+ * Returns the sum of the available values.
+ * <p>
+ * If there are no values in the dataset, or 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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 = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Sum} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Sum} instance to copy
+ */
+ public Sum(Sum original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n == 0) {
+ value = d;
+ } else {
+ value += d;
+ }
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = Double.NaN;
+ n = 0;
+ }
+
+ /**
+ * 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.</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 Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ double sum = Double.NaN;
+ if (test(values, begin, length)) {
+ 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 <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <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>
+ * 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 Double.NaN if length = 0
+ * @throws IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) {
+ double sum = Double.NaN;
+ if (test(values, weights, begin, length)) {
+ 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>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>
+ * </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 IllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights) {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Sum copy() {
+ Sum result = new Sum();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(Sum source, Sum dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java b/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.java
new file mode 100644
index 0000000..331d5d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfLogs.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.math.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Returns the sum of the natural logs for this collection of values.
+ * <p>
+ * Uses {@link java.lang.Math#log(double)} to compute the logs. Therefore,
+ * <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>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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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
+ */
+ public SumOfLogs(SumOfLogs original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ value += FastMath.log(d);
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ if (n > 0) {
+ return value;
+ } else {
+ return Double.NaN;
+ }
+ }
+
+ /**
+ * {@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>IllegalArgumentException</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 Double.NaN if
+ * length = 0
+ * @throws IllegalArgumentException 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) {
+ double sumLog = Double.NaN;
+ if (test(values, begin, length)) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SumOfLogs source, SumOfLogs dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java b/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.java
new file mode 100644
index 0000000..a632bf6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/summary/SumOfSquares.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.math.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
+
+/**
+ * Returns the sum of the squares of the available values.
+ * <p>
+ * If there are no values in the dataset, or 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>
+ *
+ * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
+ */
+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 = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code SumOfSquares} identical
+ * to the {@code original}
+ *
+ * @param original the {@code SumOfSquares} instance to copy
+ */
+ public SumOfSquares(SumOfSquares original) {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n == 0) {
+ value = d * d;
+ } else {
+ value += d * d;
+ }
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = Double.NaN;
+ 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>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 sum of the squares of the values or Double.NaN if length = 0
+ * @throws IllegalArgumentException 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) {
+ double sumSq = Double.NaN;
+ if (test(values, begin, length)) {
+ 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();
+ 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 NullPointerException if either source or dest is null
+ */
+ public static void copy(SumOfSquares source, SumOfSquares dest) {
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/descriptive/summary/package.html b/src/main/java/org/apache/commons/math/stat/descriptive/summary/package.html
new file mode 100644
index 0000000..db7f731
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/descriptive/summary/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Other summary statistics.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTest.java b/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTest.java
new file mode 100644
index 0000000..6a3ecac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTest.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.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * An interface for Chi-Square tests.
+ * <p>This interface handles only known distributions. If the distribution is
+ * unknown and should be provided by a sample, then the {@link UnknownDistributionChiSquareTest
+ * UnknownDistributionChiSquareTest} extended interface should be used instead.</p>
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public interface ChiSquareTest {
+
+ /**
+ * 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 >= 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>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return chiSquare statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ */
+ double chiSquare(double[] expected, long[] observed)
+ throws IllegalArgumentException;
+
+ /**
+ * 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 >= 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>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return p-value
+ * @throws IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double chiSquareTest(double[] expected, long[] observed)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * 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 >= 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 </code>
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean chiSquareTest(double[] expected, long[] observed, double alpha)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * 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 >= 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 statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ */
+ double chiSquare(long[][] counts)
+ throws IllegalArgumentException;
+
+ /**
+ * 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 >= 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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double chiSquareTest(long[][] counts)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * 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 >= 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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean chiSquareTest(long[][] counts, double alpha)
+ throws IllegalArgumentException, MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java b/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java
new file mode 100644
index 0000000..abb32a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/ChiSquareTestImpl.java
@@ -0,0 +1,424 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.distribution.ChiSquaredDistribution;
+import org.apache.commons.math.distribution.ChiSquaredDistributionImpl;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements Chi-Square test statistics defined in the
+ * {@link UnknownDistributionChiSquareTest} interface.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class ChiSquareTestImpl implements UnknownDistributionChiSquareTest {
+
+ /** Distribution used to compute inference statistics. */
+ private ChiSquaredDistribution distribution;
+
+ /**
+ * Construct a ChiSquareTestImpl
+ */
+ public ChiSquareTestImpl() {
+ this(new ChiSquaredDistributionImpl(1.0));
+ }
+
+ /**
+ * Create a test instance using the given distribution for computing
+ * inference statistics.
+ * @param x distribution used to compute inference statistics.
+ * @since 1.2
+ */
+ public ChiSquareTestImpl(ChiSquaredDistribution x) {
+ super();
+ setDistribution(x);
+ }
+ /**
+ * {@inheritDoc}
+ * <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 chi-square test statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ * or length is less than 2
+ */
+ public double chiSquare(double[] expected, long[] observed)
+ throws IllegalArgumentException {
+ if (expected.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, expected.length, 2);
+ }
+ if (expected.length != observed.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, expected.length, observed.length);
+ }
+ checkPositive(expected);
+ 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;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double chiSquareTest(double[] expected, long[] observed)
+ throws IllegalArgumentException, MathException {
+ distribution.setDegreesOfFreedom(expected.length - 1.0);
+ return 1.0 - distribution.cumulativeProbability(
+ chiSquare(expected, observed));
+ }
+
+ /**
+ * {@inheritDoc}
+ * <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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean chiSquareTest(double[] expected, long[] observed,
+ double alpha) throws IllegalArgumentException, MathException {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return chiSquareTest(expected, observed) < alpha;
+ }
+
+ /**
+ * @param counts array representation of 2-way table
+ * @return chi-square test statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ */
+ public double chiSquare(long[][] counts) throws IllegalArgumentException {
+
+ 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;
+ }
+
+ /**
+ * @param counts array representation of 2-way table
+ * @return p-value
+ * @throws IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double chiSquareTest(long[][] counts)
+ throws IllegalArgumentException, MathException {
+ checkArray(counts);
+ double df = ((double) counts.length -1) * ((double) counts[0].length - 1);
+ distribution.setDegreesOfFreedom(df);
+ return 1 - distribution.cumulativeProbability(chiSquare(counts));
+ }
+
+ /**
+ * @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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean chiSquareTest(long[][] counts, double alpha)
+ throws IllegalArgumentException, MathException {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0.0, 0.5);
+ }
+ return chiSquareTest(counts) < alpha;
+ }
+
+ /**
+ * @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 chi-square test statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ * @since 1.2
+ */
+ public double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException {
+
+ // Make sure lengths are same
+ if (observed1.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, observed1.length, 2);
+ }
+ if (observed1.length != observed2.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ observed1.length, observed2.length);
+ }
+
+ // Ensure non-negative counts
+ checkNonNegative(observed1);
+ 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) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OBSERVED_COUNTS_ALL_ZERO, 1);
+ }
+ if (countSum2 == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OBSERVED_COUNTS_ALL_ZERO, 2);
+ }
+ // 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 MathRuntimeException.createIllegalArgumentException(
+ 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;
+ }
+
+ /**
+ * @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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ * @since 1.2
+ */
+ public double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException, MathException {
+ distribution.setDegreesOfFreedom((double) observed1.length - 1);
+ return 1 - distribution.cumulativeProbability(
+ chiSquareDataSetsComparison(observed1, observed2));
+ }
+
+ /**
+ * @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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ * @since 1.2
+ */
+ public boolean chiSquareTestDataSetsComparison(long[] observed1, long[] observed2,
+ double alpha) throws IllegalArgumentException, MathException {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0.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,
+ * throwing IllegalArgumentException if any of these checks fail.
+ *
+ * @param in input 2-way table to check
+ * @throws IllegalArgumentException if the array is not valid
+ */
+ private void checkArray(long[][] in) throws IllegalArgumentException {
+
+ if (in.length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, in.length, 2);
+ }
+
+ if (in[0].length < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DIMENSION, in[0].length, 2);
+ }
+
+ checkRectangular(in);
+ checkNonNegative(in);
+
+ }
+
+ //--------------------- Private array methods -- should find a utility home for these
+
+ /**
+ * Throws IllegalArgumentException if the input array is not rectangular.
+ *
+ * @param in array to be tested
+ * @throws NullPointerException if input array is null
+ * @throws IllegalArgumentException if input array is not rectangular
+ */
+ private void checkRectangular(long[][] in) {
+ for (int i = 1; i < in.length; i++) {
+ if (in[i].length != in[0].length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ in[i].length, in[0].length);
+ }
+ }
+ }
+
+ /**
+ * Check all entries of the input array are > 0.
+ *
+ * @param in array to be tested
+ * @exception IllegalArgumentException if one entry is not positive
+ */
+ private void checkPositive(double[] in) throws IllegalArgumentException {
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_ELEMENT_AT_INDEX,
+ i, in[i]);
+ }
+ }
+ }
+
+ /**
+ * Check all entries of the input array are >= 0.
+ *
+ * @param in array to be tested
+ * @exception IllegalArgumentException if one entry is negative
+ */
+ private void checkNonNegative(long[] in) throws IllegalArgumentException {
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX,
+ i, in[i]);
+ }
+ }
+ }
+
+ /**
+ * Check all entries of the input array are >= 0.
+ *
+ * @param in array to be tested
+ * @exception IllegalArgumentException if one entry is negative
+ */
+ private void checkNonNegative(long[][] in) throws IllegalArgumentException {
+ for (int i = 0; i < in.length; i ++) {
+ for (int j = 0; j < in[i].length; j++) {
+ if (in[i][j] < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_ELEMENT_AT_2D_INDEX,
+ i, j, in[i][j]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Modify the distribution used to compute inference statistics.
+ *
+ * @param value
+ * the new distribution
+ * @since 1.2
+ */
+ public void setDistribution(ChiSquaredDistribution value) {
+ distribution = value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/OneWayAnova.java b/src/main/java/org/apache/commons/math/stat/inference/OneWayAnova.java
new file mode 100644
index 0000000..a2cde47
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/OneWayAnova.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.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+import java.util.Collection;
+
+/**
+ * An interface for one-way ANOVA (analysis of variance).
+ *
+ * <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.math.stat.inference.TTest}.
+ * </p>
+ *
+ * @since 1.2
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+public interface 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>
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @return Fvalue
+ * @throws IllegalArgumentException if the preconditions are not met
+ * @throws MathException if the statistic can not be computed do to a
+ * convergence or other numerical error.
+ */
+ double anovaFValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * 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>
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @return Pvalue
+ * @throws IllegalArgumentException if the preconditions are not met
+ * @throws MathException if the statistic can not be computed do to a
+ * convergence or other numerical error.
+ */
+ double anovaPValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * 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>
+ *
+ * @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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if the statistic can not be computed do to a
+ * convergence or other numerical error.
+ */
+ boolean anovaTest(Collection<double[]> categoryData, double alpha)
+ throws IllegalArgumentException, MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java b/src/main/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.java
new file mode 100644
index 0000000..a47d0cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/OneWayAnovaImpl.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.math.stat.inference;
+
+import java.util.Collection;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.distribution.FDistribution;
+import org.apache.commons.math.distribution.FDistributionImpl;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.stat.descriptive.summary.SumOfSquares;
+
+
+/**
+ * Implements one-way ANOVA statistics defined in the {@link OneWayAnovaImpl}
+ * interface.
+ *
+ * <p>Uses the
+ * {@link org.apache.commons.math.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
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class OneWayAnovaImpl implements OneWayAnova {
+
+ /**
+ * Default constructor.
+ */
+ public OneWayAnovaImpl() {
+ }
+
+ /**
+ * {@inheritDoc}<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>
+ */
+ public double anovaFValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException {
+ AnovaStats a = anovaStats(categoryData);
+ return a.F;
+ }
+
+ /**
+ * {@inheritDoc}<p>
+ * This implementation uses the
+ * {@link org.apache.commons.math.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>
+ */
+ public double anovaPValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException {
+ AnovaStats a = anovaStats(categoryData);
+ FDistribution fdist = new FDistributionImpl(a.dfbg, a.dfwg);
+ return 1.0 - fdist.cumulativeProbability(a.F);
+ }
+
+ /**
+ * {@inheritDoc}<p>
+ * This implementation uses the
+ * {@link org.apache.commons.math.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>
+ */
+ public boolean anovaTest(Collection<double[]> categoryData, double alpha)
+ throws IllegalArgumentException, MathException {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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
+ * @return computed AnovaStats
+ * @throws IllegalArgumentException if categoryData does not meet
+ * preconditions specified in the interface definition
+ * @throws MathException if an error occurs computing the Anova stats
+ */
+ private AnovaStats anovaStats(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException {
+
+ // check if we have enough categories
+ if (categoryData.size() < 2) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.TWO_OR_MORE_CATEGORIES_REQUIRED,
+ categoryData.size());
+ }
+
+ // check if each category has enough data and all is double[]
+ for (double[] array : categoryData) {
+ if (array.length <= 1) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED,
+ array.length);
+ }
+ }
+
+ int dfwg = 0;
+ double sswg = 0;
+ Sum totsum = new Sum();
+ SumOfSquares totsumsq = new SumOfSquares();
+ int totnum = 0;
+
+ for (double[] data : categoryData) {
+
+ Sum sum = new Sum();
+ SumOfSquares sumsq = new SumOfSquares();
+ int num = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ double val = data[i];
+
+ // within category
+ num++;
+ sum.increment(val);
+ sumsq.increment(val);
+
+ // for all categories
+ totnum++;
+ totsum.increment(val);
+ totsumsq.increment(val);
+ }
+ dfwg += num - 1;
+ double ss = sumsq.getResult() - sum.getResult() * sum.getResult() / num;
+ sswg += ss;
+ }
+ double sst = totsumsq.getResult() - totsum.getResult() *
+ totsum.getResult()/totnum;
+ double ssbg = sst - sswg;
+ int dfbg = categoryData.size() - 1;
+ double msbg = ssbg/dfbg;
+ double mswg = sswg/dfwg;
+ double F = msbg/mswg;
+
+ return new AnovaStats(dfbg, dfwg, F);
+ }
+
+ /**
+ Convenience class to pass dfbg,dfwg,F values around within AnovaImpl.
+ No get/set methods provided.
+ */
+ private static class AnovaStats {
+
+ /** Degrees of freedom in numerator (between groups). */
+ private int dfbg;
+
+ /** Degrees of freedom in denominator (within groups). */
+ private int dfwg;
+
+ /** Statistic. */
+ private 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/math/stat/inference/TTest.java b/src/main/java/org/apache/commons/math/stat/inference/TTest.java
new file mode 100644
index 0000000..0ccb0c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/TTest.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.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.stat.descriptive.StatisticalSummary;
+
+/**
+ * An interface 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>
+ *
+ *
+ * @version $Revision: 811786 $ $Date: 2009-09-06 11:36:08 +0200 (dim. 06 sept. 2009) $
+ */
+public interface 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if the statistic can not be computed do to a
+ * convergence or other numerical error.
+ */
+ double pairedT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double pairedTTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 < 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean pairedTTest(
+ double[] sample1,
+ double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if input array length is less than 2
+ */
+ double t(double mu, double[] observed)
+ throws IllegalArgumentException;
+ /**
+ * 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() > = 2</code>.
+ * </li></ul></p>
+ *
+ * @param mu comparison constant
+ * @param sampleStats DescriptiveStatistics holding sample summary statitstics
+ * @return t statistic
+ * @throws IllegalArgumentException if the precondition is not met
+ */
+ double t(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException;
+ /**
+ * 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ double homoscedasticT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException;
+ /**
+ * 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ double t(double[] sample1, double[] sample2)
+ throws IllegalArgumentException;
+ /**
+ * 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ double t(
+ StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException;
+ /**
+ * 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ double homoscedasticT(
+ StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double tTest(double mu, double[] sample)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error computing the p-value
+ */
+ boolean tTest(double mu, double[] sample, double alpha)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double tTest(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ boolean tTest(
+ double mu,
+ StatisticalSummary sampleStats,
+ double alpha)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double tTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double homoscedasticTTest(
+ double[] sample1,
+ double[] sample2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean tTest(
+ double[] sample1,
+ double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean homoscedasticTTest(
+ double[] sample1,
+ double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 popuation 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double tTest(
+ StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double homoscedasticTTest(
+ StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException;
+ /**
+ * 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean tTest(
+ StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2,
+ double alpha)
+ throws IllegalArgumentException, MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/TTestImpl.java b/src/main/java/org/apache/commons/math/stat/inference/TTestImpl.java
new file mode 100644
index 0000000..d4d1a12
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/TTestImpl.java
@@ -0,0 +1,1069 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.distribution.TDistribution;
+import org.apache.commons.math.distribution.TDistributionImpl;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.stat.StatUtils;
+import org.apache.commons.math.stat.descriptive.StatisticalSummary;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements t-test statistics defined in the {@link TTest} interface.
+ * <p>
+ * Uses commons-math {@link org.apache.commons.math.distribution.TDistributionImpl}
+ * implementation to estimate exact p-values.</p>
+ *
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ */
+public class TTestImpl implements TTest {
+
+ /** Distribution used to compute inference statistics.
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ private TDistribution distribution;
+
+ /**
+ * Default constructor.
+ */
+ public TTestImpl() {
+ this(new TDistributionImpl(1.0));
+ }
+
+ /**
+ * Create a test instance using the given distribution for computing
+ * inference statistics.
+ * @param t distribution used to compute inference statistics.
+ * @since 1.2
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public TTestImpl(TDistribution t) {
+ super();
+ setDistribution(t);
+ }
+
+ /**
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if the statistic can not be computed do to a
+ * convergence or other numerical error.
+ */
+ public double pairedT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double pairedTTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ 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 < 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean pairedTTest(double[] sample1, double[] sample2, double alpha)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if input array length is less than 2
+ */
+ public double t(double mu, double[] observed)
+ throws IllegalArgumentException {
+ checkSampleData(observed);
+ 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() > = 2</code>.
+ * </li></ul></p>
+ *
+ * @param mu comparison constant
+ * @param sampleStats DescriptiveStatistics holding sample summary statitstics
+ * @return t statistic
+ * @throws IllegalArgumentException if the precondition is not met
+ */
+ public double t(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException {
+ 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ public double homoscedasticT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException {
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ public double t(double[] sample1, double[] sample2)
+ throws IllegalArgumentException {
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ 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-statisitc 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 IllegalArgumentException if the precondition is not met
+ */
+ public double t(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException {
+ 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-statisitc 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>
+ * 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 IllegalArgumentException if the precondition is not met
+ */
+ public double homoscedasticT(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double tTest(double mu, double[] sample)
+ throws IllegalArgumentException, MathException {
+ checkSampleData(sample);
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error computing the p-value
+ */
+ public boolean tTest(double mu, double[] sample, double alpha)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double tTest(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public boolean tTest( double mu, StatisticalSummary sampleStats,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double tTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ 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>
+ * 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double homoscedasticTTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean tTest(double[] sample1, double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean homoscedasticTTest(double[] sample1, double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ 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 popuation 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double tTest(StatisticalSummary sampleStats1, StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the precondition is not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ public double homoscedasticTTest(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException {
+ 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 IllegalArgumentException if the preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ public boolean tTest(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2, double alpha)
+ throws IllegalArgumentException, MathException {
+ 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(double m, double mu, double v, 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(double m1, double m2, double v1, double v2, double n1,
+ 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(double m1, double m2, double v1,
+ double v2, double n1, double n2) {
+ 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 MathException if an error occurs computing the p-value
+ */
+ protected double tTest(double m, double mu, double v, double n)
+ throws MathException {
+ double t = FastMath.abs(t(m, mu, v, n));
+ distribution.setDegreesOfFreedom(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 MathException if an error occurs computing the p-value
+ */
+ protected double tTest(double m1, double m2, double v1, double v2,
+ double n1, double n2)
+ throws MathException {
+ double t = FastMath.abs(t(m1, m2, v1, v2, n1, n2));
+ double degreesOfFreedom = 0;
+ degreesOfFreedom = df(v1, v2, n1, n2);
+ distribution.setDegreesOfFreedom(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 MathException if an error occurs computing the p-value
+ */
+ protected double homoscedasticTTest(double m1, double m2, double v1,
+ double v2, double n1, double n2)
+ throws MathException {
+ double t = FastMath.abs(homoscedasticT(m1, m2, v1, v2, n1, n2));
+ double degreesOfFreedom = n1 + n2 - 2;
+ distribution.setDegreesOfFreedom(degreesOfFreedom);
+ return 2.0 * distribution.cumulativeProbability(-t);
+ }
+
+ /**
+ * Modify the distribution used to compute inference statistics.
+ * @param value the new distribution
+ * @since 1.2
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public void setDistribution(TDistribution value) {
+ distribution = value;
+ }
+
+ /** Check significance level.
+ * @param alpha significance level
+ * @exception IllegalArgumentException if significance level is out of bounds
+ */
+ private void checkSignificanceLevel(final double alpha)
+ throws IllegalArgumentException {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0.0, 0.5);
+ }
+ }
+
+ /** Check sample data.
+ * @param data sample data
+ * @exception IllegalArgumentException if there is not enough sample data
+ */
+ private void checkSampleData(final double[] data)
+ throws IllegalArgumentException {
+ if ((data == null) || (data.length < 2)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DATA_FOR_T_STATISTIC,
+ (data == null) ? 0 : data.length);
+ }
+ }
+
+ /** Check sample data.
+ * @param stat statistical summary
+ * @exception IllegalArgumentException if there is not enough sample data
+ */
+ private void checkSampleData(final StatisticalSummary stat)
+ throws IllegalArgumentException {
+ if ((stat == null) || (stat.getN() < 2)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_DATA_FOR_T_STATISTIC,
+ (stat == null) ? 0 : stat.getN());
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/TestUtils.java b/src/main/java/org/apache/commons/math/stat/inference/TestUtils.java
new file mode 100644
index 0000000..5023d55
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/TestUtils.java
@@ -0,0 +1,436 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.inference;
+
+import java.util.Collection;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.stat.descriptive.StatisticalSummary;
+
+/**
+ * A collection of static methods to create inference test instances or to
+ * perform inference tests.
+ *
+ * <p>
+ * The set methods are not compatible with using the class in multiple threads,
+ * and have therefore been deprecated (along with the getters).
+ * The setters and getters will be removed in version 3.0.
+ *
+ * @since 1.1
+ * @version $Revision: 1067582 $ $Date: 2011-02-06 04:55:32 +0100 (dim. 06 févr. 2011) $
+ */
+public class TestUtils {
+
+ /** Singleton TTest instance using default implementation. */
+ private static TTest tTest = new TTestImpl();
+
+ /** Singleton ChiSquareTest instance using default implementation. */
+ private static ChiSquareTest chiSquareTest =
+ new ChiSquareTestImpl();
+
+ /** Singleton ChiSquareTest instance using default implementation. */
+ private static UnknownDistributionChiSquareTest unknownDistributionChiSquareTest =
+ new ChiSquareTestImpl();
+
+ /** Singleton OneWayAnova instance using default implementation. */
+ private static OneWayAnova oneWayAnova =
+ new OneWayAnovaImpl();
+
+ /**
+ * Prevent instantiation.
+ */
+ protected TestUtils() {
+ super();
+ }
+
+ /**
+ * Set the (singleton) TTest instance.
+ *
+ * @param chiSquareTest the new instance to use
+ * @since 1.2
+ * @deprecated 2.2 will be removed in 3.0 - not compatible with use from multiple threads
+ */
+ @Deprecated
+ public static void setChiSquareTest(TTest chiSquareTest) {
+ TestUtils.tTest = chiSquareTest;
+ }
+
+ /**
+ * Return a (singleton) TTest instance. Does not create a new instance.
+ *
+ * @return a TTest instance
+ * @deprecated 2.2 will be removed in 3.0
+ */
+ @Deprecated
+ public static TTest getTTest() {
+ return tTest;
+ }
+
+ /**
+ * Set the (singleton) ChiSquareTest instance.
+ *
+ * @param chiSquareTest the new instance to use
+ * @since 1.2
+ * @deprecated 2.2 will be removed in 3.0 - not compatible with use from multiple threads
+ */
+ @Deprecated
+ public static void setChiSquareTest(ChiSquareTest chiSquareTest) {
+ TestUtils.chiSquareTest = chiSquareTest;
+ }
+
+ /**
+ * Return a (singleton) ChiSquareTest instance. Does not create a new instance.
+ *
+ * @return a ChiSquareTest instance
+ * @deprecated 2.2 will be removed in 3.0
+ */
+ @Deprecated
+ public static ChiSquareTest getChiSquareTest() {
+ return chiSquareTest;
+ }
+
+ /**
+ * Set the (singleton) UnknownDistributionChiSquareTest instance.
+ *
+ * @param unknownDistributionChiSquareTest the new instance to use
+ * @since 1.2
+ * @deprecated 2.2 will be removed in 3.0 - not compatible with use from multiple threads
+ */
+ @Deprecated
+ public static void setUnknownDistributionChiSquareTest(UnknownDistributionChiSquareTest unknownDistributionChiSquareTest) {
+ TestUtils.unknownDistributionChiSquareTest = unknownDistributionChiSquareTest;
+ }
+
+ /**
+ * Return a (singleton) UnknownDistributionChiSquareTest instance. Does not create a new instance.
+ *
+ * @return a UnknownDistributionChiSquareTest instance
+ * @deprecated 2.2 will be removed in 3.0
+ */
+ @Deprecated
+ public static UnknownDistributionChiSquareTest getUnknownDistributionChiSquareTest() {
+ return unknownDistributionChiSquareTest;
+ }
+
+ /**
+ * Set the (singleton) OneWayAnova instance
+ *
+ * @param oneWayAnova the new instance to use
+ * @since 1.2
+ * @deprecated 2.2 will be removed in 3.0 - not compatible with use from multiple threads
+ */
+ @Deprecated
+ public static void setOneWayAnova(OneWayAnova oneWayAnova) {
+ TestUtils.oneWayAnova = oneWayAnova;
+ }
+
+ /**
+ * Return a (singleton) OneWayAnova instance. Does not create a new instance.
+ *
+ * @return a OneWayAnova instance
+ * @since 1.2
+ * @deprecated 2.2 will be removed in 3.0
+ */
+ @Deprecated
+ public static OneWayAnova getOneWayAnova() {
+ return oneWayAnova;
+ }
+
+
+ // CHECKSTYLE: stop JavadocMethodCheck
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#homoscedasticT(double[], double[])
+ */
+ public static double homoscedasticT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException {
+ return tTest.homoscedasticT(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#homoscedasticT(org.apache.commons.math.stat.descriptive.StatisticalSummary, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double homoscedasticT(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException {
+ return tTest.homoscedasticT(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#homoscedasticTTest(double[], double[], double)
+ */
+ public static boolean homoscedasticTTest(double[] sample1, double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest. homoscedasticTTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#homoscedasticTTest(double[], double[])
+ */
+ public static double homoscedasticTTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ return tTest.homoscedasticTTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#homoscedasticTTest(org.apache.commons.math.stat.descriptive.StatisticalSummary, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double homoscedasticTTest(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException {
+ return tTest.homoscedasticTTest(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#pairedT(double[], double[])
+ */
+ public static double pairedT(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ return tTest.pairedT(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#pairedTTest(double[], double[], double)
+ */
+ public static boolean pairedTTest(double[] sample1, double[] sample2,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest.pairedTTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#pairedTTest(double[], double[])
+ */
+ public static double pairedTTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ return tTest.pairedTTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#t(double, double[])
+ */
+ public static double t(double mu, double[] observed)
+ throws IllegalArgumentException {
+ return tTest.t(mu, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#t(double, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double t(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException {
+ return tTest.t(mu, sampleStats);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#t(double[], double[])
+ */
+ public static double t(double[] sample1, double[] sample2)
+ throws IllegalArgumentException {
+ return tTest.t(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#t(org.apache.commons.math.stat.descriptive.StatisticalSummary, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double t(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException {
+ return tTest.t(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double, double[], double)
+ */
+ public static boolean tTest(double mu, double[] sample, double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(mu, sample, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double, double[])
+ */
+ public static double tTest(double mu, double[] sample)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(mu, sample);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double, org.apache.commons.math.stat.descriptive.StatisticalSummary, double)
+ */
+ public static boolean tTest(double mu, StatisticalSummary sampleStats,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest. tTest(mu, sampleStats, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double tTest(double mu, StatisticalSummary sampleStats)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(mu, sampleStats);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double[], double[], double)
+ */
+ public static boolean tTest(double[] sample1, double[] sample2, double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(double[], double[])
+ */
+ public static double tTest(double[] sample1, double[] sample2)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(org.apache.commons.math.stat.descriptive.StatisticalSummary, org.apache.commons.math.stat.descriptive.StatisticalSummary, double)
+ */
+ public static boolean tTest(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2, double alpha)
+ throws IllegalArgumentException, MathException {
+ return tTest. tTest(sampleStats1, sampleStats2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.TTest#tTest(org.apache.commons.math.stat.descriptive.StatisticalSummary, org.apache.commons.math.stat.descriptive.StatisticalSummary)
+ */
+ public static double tTest(StatisticalSummary sampleStats1,
+ StatisticalSummary sampleStats2)
+ throws IllegalArgumentException, MathException {
+ return tTest.tTest(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquare(double[], long[])
+ */
+ public static double chiSquare(double[] expected, long[] observed)
+ throws IllegalArgumentException {
+ return chiSquareTest.chiSquare(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquare(long[][])
+ */
+ public static double chiSquare(long[][] counts)
+ throws IllegalArgumentException {
+ return chiSquareTest.chiSquare(counts);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquareTest(double[], long[], double)
+ */
+ public static boolean chiSquareTest(double[] expected, long[] observed,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ return chiSquareTest.chiSquareTest(expected, observed, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquareTest(double[], long[])
+ */
+ public static double chiSquareTest(double[] expected, long[] observed)
+ throws IllegalArgumentException, MathException {
+ return chiSquareTest.chiSquareTest(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquareTest(long[][], double)
+ */
+ public static boolean chiSquareTest(long[][] counts, double alpha)
+ throws IllegalArgumentException, MathException {
+ return chiSquareTest. chiSquareTest(counts, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.ChiSquareTest#chiSquareTest(long[][])
+ */
+ public static double chiSquareTest(long[][] counts)
+ throws IllegalArgumentException, MathException {
+ return chiSquareTest. chiSquareTest(counts);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.UnknownDistributionChiSquareTest#chiSquareDataSetsComparison(long[], long[])
+ *
+ * @since 1.2
+ */
+ public static double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException {
+ return unknownDistributionChiSquareTest.chiSquareDataSetsComparison(observed1, observed2);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.UnknownDistributionChiSquareTest#chiSquareTestDataSetsComparison(long[], long[])
+ *
+ * @since 1.2
+ */
+ public static double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException, MathException {
+ return unknownDistributionChiSquareTest.chiSquareTestDataSetsComparison(observed1, observed2);
+ }
+
+
+ /**
+ * @see org.apache.commons.math.stat.inference.UnknownDistributionChiSquareTest#chiSquareTestDataSetsComparison(long[], long[], double)
+ *
+ * @since 1.2
+ */
+ public static boolean chiSquareTestDataSetsComparison(long[] observed1, long[] observed2,
+ double alpha)
+ throws IllegalArgumentException, MathException {
+ return unknownDistributionChiSquareTest.chiSquareTestDataSetsComparison(observed1, observed2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.OneWayAnova#anovaFValue(Collection)
+ *
+ * @since 1.2
+ */
+ public static double oneWayAnovaFValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException {
+ return oneWayAnova.anovaFValue(categoryData);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.OneWayAnova#anovaPValue(Collection)
+ *
+ * @since 1.2
+ */
+ public static double oneWayAnovaPValue(Collection<double[]> categoryData)
+ throws IllegalArgumentException, MathException {
+ return oneWayAnova.anovaPValue(categoryData);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.inference.OneWayAnova#anovaTest(Collection,double)
+ *
+ * @since 1.2
+ */
+ public static boolean oneWayAnovaTest(Collection<double[]> categoryData, double alpha)
+ throws IllegalArgumentException, MathException {
+ return oneWayAnova.anovaTest(categoryData, alpha);
+ }
+
+ // CHECKSTYLE: resume JavadocMethodCheck
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/UnknownDistributionChiSquareTest.java b/src/main/java/org/apache/commons/math/stat/inference/UnknownDistributionChiSquareTest.java
new file mode 100644
index 0000000..662e4d6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/UnknownDistributionChiSquareTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.inference;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * An interface for Chi-Square tests for unknown distributions.
+ * <p>Two samples tests are used when the distribution is unknown <i>a priori</i>
+ * but provided by one sample. We compare the second sample against the first.</p>
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @since 1.2
+ */
+public interface UnknownDistributionChiSquareTest extends ChiSquareTest {
+
+ /**
+ * <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 statistic
+ * @throws IllegalArgumentException if preconditions are not met
+ */
+ double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException;
+
+ /**
+ * <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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs computing the p-value
+ */
+ double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
+ throws IllegalArgumentException, MathException;
+
+ /**
+ * <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 IllegalArgumentException if preconditions are not met
+ * @throws MathException if an error occurs performing the test
+ */
+ boolean chiSquareTestDataSetsComparison(long[] observed1, long[] observed2, double alpha)
+ throws IllegalArgumentException, MathException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/inference/package.html b/src/main/java/org/apache/commons/math/stat/inference/package.html
new file mode 100644
index 0000000..288eebf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/inference/package.html
@@ -0,0 +1,23 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>
+ Classes providing hypothesis testing and confidence interval
+ construction.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/package.html b/src/main/java/org/apache/commons/math/stat/package.html
new file mode 100644
index 0000000..d62d67a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Data storage, manipulation and summary routines.</body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/ranking/NaNStrategy.java b/src/main/java/org/apache/commons/math/stat/ranking/NaNStrategy.java
new file mode 100644
index 0000000..cffa7d1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/ranking/NaNStrategy.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.math.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>
+ * </ul>
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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
+}
diff --git a/src/main/java/org/apache/commons/math/stat/ranking/NaturalRanking.java b/src/main/java/org/apache/commons/math/stat/ranking/NaturalRanking.java
new file mode 100644
index 0000000..f51189c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/ranking/NaturalRanking.java
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.ranking;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math.exception.MathInternalError;
+import org.apache.commons.math.random.RandomData;
+import org.apache.commons.math.random.RandomDataImpl;
+import org.apache.commons.math.random.RandomGenerator;
+import org.apache.commons.math.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#MAXIMAL} 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
+ * @version $Revision: 1061496 $ $Date: 2011-01-20 21:32:16 +0100 (jeu. 20 janv. 2011) $
+ */
+public class NaturalRanking implements RankingAlgorithm {
+
+ /** default NaN strategy */
+ public static final NaNStrategy DEFAULT_NAN_STRATEGY = NaNStrategy.MAXIMAL;
+
+ /** 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 RandomData 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 RandomDataImpl();
+ }
+
+ /**
+ * 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 RandomDataImpl();
+ }
+
+ /**
+ * 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 RandomDataImpl(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 RandomDataImpl(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
+ */
+ 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;
+ 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()) {
+ 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
+ */
+ public 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);
+ }
+
+ /**
+ * 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/math/stat/ranking/RankingAlgorithm.java b/src/main/java/org/apache/commons/math/stat/ranking/RankingAlgorithm.java
new file mode 100644
index 0000000..b01f324
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/ranking/RankingAlgorithm.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.math.stat.ranking;
+
+/**
+ * Interface representing a rank transformation.
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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/math/stat/ranking/TiesStrategy.java b/src/main/java/org/apache/commons/math/stat/ranking/TiesStrategy.java
new file mode 100644
index 0000000..794c229
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/ranking/TiesStrategy.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.math.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
+ * @version $Revision: 981332 $ $Date: 2010-08-02 00:24:31 +0200 (lun. 02 août 2010) $
+ */
+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/math/stat/ranking/package.html b/src/main/java/org/apache/commons/math/stat/ranking/package.html
new file mode 100644
index 0000000..63e0c4a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/ranking/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision:$ $Date:$ -->
+ <body>
+ Classes providing rank transformations.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java b/src/main/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.java
new file mode 100644
index 0000000..9757682
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.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.math.stat.regression;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.linear.ArrayRealVector;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Abstract base class for implementations of MultipleLinearRegression.
+ * @version $Revision: 1073459 $ $Date: 2011-02-22 20:18:12 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+ */
+public abstract class AbstractMultipleLinearRegression implements
+ MultipleLinearRegression {
+
+ /** X sample data. */
+ protected RealMatrix X;
+
+ /** Y sample data. */
+ protected RealVector Y;
+
+ /** Whether or not the regression model includes an intercept. True means no intercept. */
+ private boolean noIntercept = false;
+
+ /**
+ * @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 IllegalArgumentException if the preconditions are not met
+ */
+ public void newSampleData(double[] data, int nobs, int nvars) {
+ if (data == null) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ if (data.length != nobs * (nvars + 1)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INVALID_REGRESSION_ARRAY, data.length, nobs, nvars);
+ }
+ if (nobs <= nvars) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS);
+ }
+ 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.X = new Array2DRowRealMatrix(x);
+ this.Y = new ArrayRealVector(y);
+ }
+
+ /**
+ * Loads new y sample data, overriding any previous data.
+ *
+ * @param y the array representing the y sample
+ * @throws IllegalArgumentException if y is null or empty
+ */
+ protected void newYSampleData(double[] y) {
+ if (y == null) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ if (y.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NO_DATA);
+ }
+ this.Y = 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 IllegalArgumentException if x is null, empty or not rectangular
+ */
+ protected void newXSampleData(double[][] x) {
+ if (x == null) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ if (x.length == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NO_DATA);
+ }
+ if (noIntercept) {
+ this.X = 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
+ x[i].length, nVars);
+ }
+ xAug[i][0] = 1.0d;
+ System.arraycopy(x[i], 0, xAug[i], 1, nVars);
+ }
+ this.X = 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 IllegalArgumentException if any of the checks fail
+ *
+ */
+ protected void validateSampleData(double[][] x, double[] y) {
+ if ((x == null) || (y == null) || (x.length != y.length)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NO_DATA);
+ }
+ if (x[0].length + 1 > x.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 IllegalArgumentException if the number of rows in x is not equal
+ * to the number of rows in covariance or covariance is not square.
+ */
+ protected void validateCovarianceData(double[][] x, double[][] covariance) {
+ if (x.length != covariance.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, x.length, covariance.length);
+ }
+ if (covariance.length > 0 && covariance.length != covariance[0].length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NON_SQUARE_MATRIX,
+ covariance.length, covariance[0].length);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[] estimateRegressionParameters() {
+ RealVector b = calculateBeta();
+ return b.getData();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[] estimateResiduals() {
+ RealVector b = calculateBeta();
+ RealVector e = Y.subtract(X.operate(b));
+ return e.getData();
+ }
+
+ /**
+ * {@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 Math.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(Y.getData());
+ }
+
+ /**
+ * <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) /
+ (X.getRowDimension() - X.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 Y.subtract(X.operate(b));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java b/src/main/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.java
new file mode 100644
index 0000000..dc6ef0d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.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.math.stat.regression;
+
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.RealVector;
+
+/**
+ * The GLS implementation of the 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>
+ * @version $Revision: 1073460 $ $Date: 2011-02-22 20:22:39 +0100 (mar. 22 févr. 2011) $
+ * @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 LUDecompositionImpl(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 = X.transpose();
+ RealMatrix XTOIX = XT.multiply(OI).multiply(X);
+ RealMatrix inverse = new LUDecompositionImpl(XTOIX).getSolver().getInverse();
+ return inverse.multiply(XT).multiply(OI).operate(Y);
+ }
+
+ /**
+ * 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 = X.transpose().multiply(OI).multiply(X);
+ return new LUDecompositionImpl(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 / (X.getRowDimension() - X.getColumnDimension());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java b/src/main/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.java
new file mode 100644
index 0000000..b7aabd4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/MultipleLinearRegression.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.math.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>.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ * @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/math/stat/regression/OLSMultipleLinearRegression.java b/src/main/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.java
new file mode 100644
index 0000000..22a59e8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.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.math.stat.regression;
+
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.LUDecompositionImpl;
+import org.apache.commons.math.linear.QRDecomposition;
+import org.apache.commons.math.linear.QRDecompositionImpl;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.RealVector;
+import org.apache.commons.math.stat.StatUtils;
+import org.apache.commons.math.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 QRDecompositionImpl} 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>
+ *
+ * @version $Revision: 1073464 $ $Date: 2011-02-22 20:35:02 +0100 (mar. 22 févr. 2011) $
+ * @since 2.0
+ */
+public class OLSMultipleLinearRegression extends AbstractMultipleLinearRegression {
+
+ /** Cached QR decomposition of X matrix */
+ private QRDecomposition qr = null;
+
+ /**
+ * 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 IllegalArgumentException if the x and y array data are not
+ * compatible for the regression
+ */
+ public void newSampleData(double[] y, double[][] x) {
+ 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 QRDecompositionImpl(X);
+ }
+
+ /**
+ * <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.
+ *
+ * @return the hat matrix
+ */
+ public RealMatrix calculateHat() {
+ // Create augmented identity matrix
+ RealMatrix Q = qr.getQ();
+ final int p = qr.getR().getColumnDimension();
+ final int n = Q.getColumnDimension();
+ 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
+ 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
+ * @see #isNoIntercept()
+ * @since 2.2
+ */
+ public double calculateTotalSumOfSquares() {
+ if (isNoIntercept()) {
+ return StatUtils.sumSq(Y.getData());
+ } else {
+ return new SecondMoment().evaluate(Y.getData());
+ }
+ }
+
+ /**
+ * Returns the sum of squared residuals.
+ *
+ * @return residual sum of squares
+ * @since 2.2
+ */
+ public double calculateResidualSumOfSquares() {
+ final RealVector residuals = calculateResiduals();
+ 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}
+ *
+ * @return R-square statistic
+ * @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>
+ *
+ * @return adjusted R-Squared statistic
+ * @see #isNoIntercept()
+ * @since 2.2
+ */
+ public double calculateAdjustedRSquared() {
+ final double n = X.getRowDimension();
+ if (isNoIntercept()) {
+ return 1 - (1 - calculateRSquared()) * (n / (n - X.getColumnDimension()));
+ } else {
+ return 1 - (calculateResidualSumOfSquares() * (n - 1)) /
+ (calculateTotalSumOfSquares() * (n - X.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 QRDecompositionImpl(X);
+ }
+
+ /**
+ * Calculates the regression coefficients using OLS.
+ *
+ * @return beta
+ */
+ @Override
+ protected RealVector calculateBeta() {
+ return qr.getSolver().solve(Y);
+ }
+
+ /**
+ * <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>
+ *
+ * @return The beta variance-covariance matrix
+ */
+ @Override
+ protected RealMatrix calculateBetaVariance() {
+ int p = X.getColumnDimension();
+ RealMatrix Raug = qr.getR().getSubMatrix(0, p - 1 , 0, p - 1);
+ RealMatrix Rinv = new LUDecompositionImpl(Raug).getSolver().getInverse();
+ return Rinv.multiply(Rinv.transpose());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/stat/regression/SimpleRegression.java b/src/main/java/org/apache/commons/math/stat/regression/SimpleRegression.java
new file mode 100644
index 0000000..d950541
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/SimpleRegression.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.regression;
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.distribution.TDistribution;
+import org.apache.commons.math.distribution.TDistributionImpl;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * 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 requred 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>
+ * </ul></p>
+ *
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ */
+public class SimpleRegression implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3004689053607543335L;
+
+ /** the distribution used to compute inference statistics. */
+ private TDistribution distribution;
+
+ /** 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;
+
+ // ---------------------Public methods--------------------------------------
+
+ /**
+ * Create an empty SimpleRegression instance
+ */
+ public SimpleRegression() {
+ this(new TDistributionImpl(1.0));
+ }
+
+ /**
+ * Create an empty SimpleRegression using the given distribution object to
+ * compute inference statistics.
+ * @param t the distribution used to compute inference statistics.
+ * @since 1.2
+ * @deprecated in 2.2 (to be removed in 3.0). Please use the {@link
+ * #SimpleRegression(int) other constructor} instead.
+ */
+ @Deprecated
+ public SimpleRegression(TDistribution t) {
+ super();
+ setDistribution(t);
+ }
+
+ /**
+ * Create an empty SimpleRegression.
+ *
+ * @param degrees Number of degrees of freedom of the distribution
+ * used to compute inference statistics.
+ * @since 2.2
+ */
+ public SimpleRegression(int degrees) {
+ setDistribution(new TDistributionImpl(degrees));
+ }
+
+ /**
+ * 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(double x, double y) {
+ if (n == 0) {
+ xbar = x;
+ ybar = y;
+ } else {
+ double dx = x - xbar;
+ double dy = y - ybar;
+ sumXX += dx * dx * (double) n / (n + 1d);
+ sumYY += dy * dy * (double) n / (n + 1d);
+ sumXY += dx * dy * (double) n / (n + 1d);
+ xbar += dx / (n + 1.0);
+ ybar += dy / (n + 1.0);
+ }
+ sumX += x;
+ sumY += y;
+ n++;
+
+ if (n > 2) {
+ distribution.setDegreesOfFreedom(n - 2);
+ }
+ }
+
+
+ /**
+ * 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(double x, double y) {
+ if (n > 0) {
+ double dx = x - xbar;
+ double dy = y - ybar;
+ sumXX -= dx * dx * (double) n / (n - 1d);
+ sumYY -= dy * dy * (double) n / (n - 1d);
+ sumXY -= dx * dy * (double) n / (n - 1d);
+ xbar -= dx / (n - 1.0);
+ ybar -= dy / (n - 1.0);
+ sumX -= x;
+ sumY -= y;
+ n--;
+
+ if (n > 2) {
+ distribution.setDegreesOfFreedom(n - 2);
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ public void addData(double[][] data) {
+ for (int i = 0; i < data.length; i++) {
+ addData(data[i][0], data[i][1]);
+ }
+ }
+
+
+ /**
+ * 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(double x) {
+ double b1 = getSlope();
+ return getIntercept(b1) + b1 * x;
+ }
+
+ /**
+ * Returns the intercept of the estimated regression line.
+ * <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
+ */
+ public double getIntercept() {
+ return getIntercept(getSlope());
+ }
+
+ /**
+ * 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 getSumSquaredErrors() / (n - 2);
+ }
+
+ /**
+ * 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>
+ *
+ * @return standard error associated with intercept estimate
+ */
+ public double getInterceptStdErr() {
+ return FastMath.sqrt(
+ getMeanSquareError() * ((1d / (double) 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 MathException if the confidence interval can not be computed.
+ */
+ public double getSlopeConfidenceInterval() throws MathException {
+ 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>IllegalArgumentException</code> is thrown.
+ * </li></ul></p>
+ *
+ * @param alpha the desired significance level
+ * @return half-width of 95% confidence interval for the slope estimate
+ * @throws MathException if the confidence interval can not be computed.
+ */
+ public double getSlopeConfidenceInterval(double alpha)
+ throws MathException {
+ if (alpha >= 1 || alpha <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0.0, 1.0);
+ }
+ 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 MathException if the significance level can not be computed.
+ */
+ public double getSignificance() throws MathException {
+ 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(double slope) {
+ return (sumY - slope * sumX) / n;
+ }
+
+ /**
+ * Computes SSR from b1.
+ *
+ * @param slope regression slope estimate
+ * @return sum of squared deviations of predicted y values
+ */
+ private double getRegressionSumSquares(double slope) {
+ return slope * slope * sumXX;
+ }
+
+ /**
+ * Modify the distribution used to compute inference statistics.
+ * @param value the new distribution
+ * @since 1.2
+ * @deprecated in 2.2 (to be removed in 3.0).
+ */
+ @Deprecated
+ public void setDistribution(TDistribution value) {
+ distribution = value;
+
+ // modify degrees of freedom
+ if (n > 2) {
+ distribution.setDegreesOfFreedom(n - 2);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/stat/regression/package.html b/src/main/java/org/apache/commons/math/stat/regression/package.html
new file mode 100644
index 0000000..2538c6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/stat/regression/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>
+ Statistical routines involving multivariate data.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/transform/FastCosineTransformer.java b/src/main/java/org/apache/commons/math/transform/FastCosineTransformer.java
new file mode 100644
index 0000000..bda0fe2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/FastCosineTransformer.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.math.transform;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <a href="http://documents.wolfram.com/v5/Add-onsLinks/
+ * StandardPackages/LinearAlgebra/FourierTrig.html">Fast Cosine Transform</a>
+ * for transformation of one-dimensional data sets. For reference, see
+ * <b>Fast Fourier Transforms</b>, ISBN 0849371635, chapter 3.
+ * <p>
+ * FCT is its own inverse, up to a multiplier depending on conventions.
+ * The equations are listed in the comments of the corresponding methods.</p>
+ * <p>
+ * Different from FFT and FST, FCT requires the length of data set to be
+ * power of 2 plus one. Users should especially pay attention to the
+ * function transformation on how this affects the sampling.</p>
+ * <p>As of version 2.0 this no longer implements Serializable</p>
+ *
+ * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $
+ * @since 1.2
+ */
+public class FastCosineTransformer implements RealTransformer {
+
+ /**
+ * Construct a default transformer.
+ */
+ public FastCosineTransformer() {
+ super();
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is F<sub>n</sub> = (1/2) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * &sum;<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform(double f[]) throws IllegalArgumentException {
+ return fct(f);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is F<sub>n</sub> = (1/2) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * &sum;<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ return fct(data);
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is F<sub>n</sub> = &radic;(1/2N) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * &radic;(2/N) &sum;<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform2(double f[]) throws IllegalArgumentException {
+
+ double scaling_coefficient = FastMath.sqrt(2.0 / (f.length-1));
+ return FastFourierTransformer.scaleArray(fct(f), scaling_coefficient);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is F<sub>n</sub> = &radic;(1/2N) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * &radic;(2/N) &sum;<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(&pi; nk/N)
+ *
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform2(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ double scaling_coefficient = FastMath.sqrt(2.0 / (n-1));
+ return FastFourierTransformer.scaleArray(fct(data), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is f<sub>k</sub> = (1/N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * (2/N) &sum;<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the real inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform(double f[]) throws IllegalArgumentException {
+
+ double scaling_coefficient = 2.0 / (f.length - 1);
+ return FastFourierTransformer.scaleArray(fct(f), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is f<sub>k</sub> = (1/N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * (2/N) &sum;<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ double scaling_coefficient = 2.0 / (n - 1);
+ return FastFourierTransformer.scaleArray(fct(data), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is f<sub>k</sub> = &radic;(1/2N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * &radic;(2/N) &sum;<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the real inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform2(double f[]) throws IllegalArgumentException {
+ return transform2(f);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is f<sub>k</sub> = &radic;(1/2N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * &radic;(2/N) &sum;<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform2(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ return transform2(f, min, max, n);
+ }
+
+ /**
+ * Perform the FCT algorithm (including inverse).
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ protected double[] fct(double f[])
+ throws IllegalArgumentException {
+
+ final double transformed[] = new double[f.length];
+
+ final int n = f.length - 1;
+ if (!FastFourierTransformer.isPowerOf2(n)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_PLUS_ONE,
+ 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];
+ double t1 = 0.5 * (f[0] - f[n]); // temporary variable for transformed[1]
+ 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 = new FastFourierTransformer();
+ Complex y[] = transformer.transform(x);
+
+ // 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/math/transform/FastFourierTransformer.java b/src/main/java/org/apache/commons/math/transform/FastFourierTransformer.java
new file mode 100644
index 0000000..e6ced5e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/FastFourierTransformer.java
@@ -0,0 +1,912 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.transform;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/FastFourierTransform.html">
+ * Fast Fourier Transform</a> for transformation of one-dimensional data sets.
+ * For reference, see <b>Applied Numerical Linear Algebra</b>, ISBN 0898713897,
+ * chapter 6.
+ * <p>
+ * There are several conventions for the definition of FFT and inverse FFT,
+ * mainly on different coefficient and exponent. Here the equations are listed
+ * in the comments of the corresponding methods.</p>
+ * <p>
+ * We require the length of data set to be 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.</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class FastFourierTransformer implements Serializable {
+
+ /** Serializable version identifier. */
+ static final long serialVersionUID = 5138259215438106000L;
+
+ /** roots of unity */
+ private RootsOfUnity roots = new RootsOfUnity();
+
+ /**
+ * Construct a default transformer.
+ */
+ public FastFourierTransformer() {
+ super();
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is $ y_n = \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k $
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform(double f[])
+ throws IllegalArgumentException {
+ return fft(f, false);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is $ y_n = \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k $
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the complex transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ double data[] = sample(f, min, max, n);
+ return fft(data, false);
+ }
+
+ /**
+ * Transform the given complex data set.
+ * <p>
+ * The formula is $ y_n = \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k $
+ * </p>
+ *
+ * @param f the complex data array to be transformed
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform(Complex f[])
+ throws IllegalArgumentException {
+ roots.computeOmega(f.length);
+ return fft(f);
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is $y_n = (1/\sqrt{N}) \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k$
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform2(double f[])
+ throws IllegalArgumentException {
+
+ double scaling_coefficient = 1.0 / FastMath.sqrt(f.length);
+ return scaleArray(fft(f, false), scaling_coefficient);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is $y_n = (1/\sqrt{N}) \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k$
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the complex transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform2(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = sample(f, min, max, n);
+ double scaling_coefficient = 1.0 / FastMath.sqrt(n);
+ return scaleArray(fft(data, false), scaling_coefficient);
+ }
+
+ /**
+ * Transform the given complex data set.
+ * <p>
+ * The formula is $y_n = (1/\sqrt{N}) \Sigma_{k=0}^{N-1} e^{-2 \pi i nk/N} x_k$
+ * </p>
+ *
+ * @param f the complex data array to be transformed
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] transform2(Complex f[])
+ throws IllegalArgumentException {
+
+ roots.computeOmega(f.length);
+ double scaling_coefficient = 1.0 / FastMath.sqrt(f.length);
+ return scaleArray(fft(f), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is $ x_k = (1/N) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n $
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the complex inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform(double f[])
+ throws IllegalArgumentException {
+
+ double scaling_coefficient = 1.0 / f.length;
+ return scaleArray(fft(f, true), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is $ x_k = (1/N) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n $
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the complex inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = sample(f, min, max, n);
+ double scaling_coefficient = 1.0 / n;
+ return scaleArray(fft(data, true), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given complex data set.
+ * <p>
+ * The formula is $ x_k = (1/N) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n $
+ * </p>
+ *
+ * @param f the complex data array to be inversely transformed
+ * @return the complex inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform(Complex f[])
+ throws IllegalArgumentException {
+
+ roots.computeOmega(-f.length); // pass negative argument
+ double scaling_coefficient = 1.0 / f.length;
+ return scaleArray(fft(f), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is $x_k = (1/\sqrt{N}) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n$
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the complex inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform2(double f[])
+ throws IllegalArgumentException {
+
+ double scaling_coefficient = 1.0 / FastMath.sqrt(f.length);
+ return scaleArray(fft(f, true), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is $x_k = (1/\sqrt{N}) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n$
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the complex inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform2(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = sample(f, min, max, n);
+ double scaling_coefficient = 1.0 / FastMath.sqrt(n);
+ return scaleArray(fft(data, true), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given complex data set.
+ * <p>
+ * The formula is $x_k = (1/\sqrt{N}) \Sigma_{n=0}^{N-1} e^{2 \pi i nk/N} y_n$
+ * </p>
+ *
+ * @param f the complex data array to be inversely transformed
+ * @return the complex inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public Complex[] inversetransform2(Complex f[])
+ throws IllegalArgumentException {
+
+ roots.computeOmega(-f.length); // pass negative argument
+ double scaling_coefficient = 1.0 / FastMath.sqrt(f.length);
+ return scaleArray(fft(f), scaling_coefficient);
+ }
+
+ /**
+ * Perform the base-4 Cooley-Tukey FFT algorithm (including inverse).
+ *
+ * @param f the real data array to be transformed
+ * @param isInverse the indicator of forward or inverse transform
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ protected Complex[] fft(double f[], boolean isInverse)
+ throws IllegalArgumentException {
+
+ verifyDataSet(f);
+ Complex F[] = new Complex[f.length];
+ if (f.length == 1) {
+ F[0] = new Complex(f[0], 0.0);
+ return F;
+ }
+
+ // Rather than the naive real to complex conversion, pack 2N
+ // real numbers into N complex numbers for better performance.
+ int N = f.length >> 1;
+ Complex c[] = new Complex[N];
+ for (int i = 0; i < N; i++) {
+ c[i] = new Complex(f[2*i], f[2*i+1]);
+ }
+ roots.computeOmega(isInverse ? -N : N);
+ Complex z[] = fft(c);
+
+ // reconstruct the FFT result for the original array
+ roots.computeOmega(isInverse ? -2*N : 2*N);
+ F[0] = new Complex(2 * (z[0].getReal() + z[0].getImaginary()), 0.0);
+ F[N] = new Complex(2 * (z[0].getReal() - z[0].getImaginary()), 0.0);
+ for (int i = 1; i < N; i++) {
+ Complex A = z[N-i].conjugate();
+ Complex B = z[i].add(A);
+ Complex C = z[i].subtract(A);
+ //Complex D = roots.getOmega(i).multiply(Complex.I);
+ Complex D = new Complex(-roots.getOmegaImaginary(i),
+ roots.getOmegaReal(i));
+ F[i] = B.subtract(C.multiply(D));
+ F[2*N-i] = F[i].conjugate();
+ }
+
+ return scaleArray(F, 0.5);
+ }
+
+ /**
+ * Perform the base-4 Cooley-Tukey FFT algorithm (including inverse).
+ *
+ * @param data the complex data array to be transformed
+ * @return the complex transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ protected Complex[] fft(Complex data[])
+ throws IllegalArgumentException {
+
+ final int n = data.length;
+ final Complex f[] = new Complex[n];
+
+ // initial simple cases
+ verifyDataSet(data);
+ if (n == 1) {
+ f[0] = data[0];
+ return f;
+ }
+ if (n == 2) {
+ f[0] = data[0].add(data[1]);
+ f[1] = data[0].subtract(data[1]);
+ return f;
+ }
+
+ // permute original data array in bit-reversal order
+ int ii = 0;
+ for (int i = 0; i < n; i++) {
+ f[i] = data[ii];
+ int k = n >> 1;
+ while (ii >= k && k > 0) {
+ ii -= k; k >>= 1;
+ }
+ ii += k;
+ }
+
+ // the bottom base-4 round
+ for (int i = 0; i < n; i += 4) {
+ final Complex a = f[i].add(f[i+1]);
+ final Complex b = f[i+2].add(f[i+3]);
+ final Complex c = f[i].subtract(f[i+1]);
+ final Complex d = f[i+2].subtract(f[i+3]);
+ final Complex e1 = c.add(d.multiply(Complex.I));
+ final Complex e2 = c.subtract(d.multiply(Complex.I));
+ f[i] = a.add(b);
+ f[i+2] = a.subtract(b);
+ // omegaCount indicates forward or inverse transform
+ f[i+1] = roots.isForward() ? e2 : e1;
+ f[i+3] = roots.isForward() ? e1 : e2;
+ }
+
+ // iterations from bottom to top take O(N*logN) time
+ for (int i = 4; i < n; i <<= 1) {
+ final int m = n / (i<<1);
+ for (int j = 0; j < n; j += i<<1) {
+ for (int k = 0; k < i; k++) {
+ //z = f[i+j+k].multiply(roots.getOmega(k*m));
+ final int k_times_m = k*m;
+ final double omega_k_times_m_real = roots.getOmegaReal(k_times_m);
+ final double omega_k_times_m_imaginary = roots.getOmegaImaginary(k_times_m);
+ //z = f[i+j+k].multiply(omega[k*m]);
+ final Complex z = new Complex(
+ f[i+j+k].getReal() * omega_k_times_m_real -
+ f[i+j+k].getImaginary() * omega_k_times_m_imaginary,
+ f[i+j+k].getReal() * omega_k_times_m_imaginary +
+ f[i+j+k].getImaginary() * omega_k_times_m_real);
+
+ f[i+j+k] = f[j+k].subtract(z);
+ f[j+k] = f[j+k].add(z);
+ }
+ }
+ }
+ return f;
+ }
+
+ /**
+ * Sample the given univariate real function on the given interval.
+ * <p>
+ * The interval is divided equally into N sections and sample points
+ * are taken from min to max-(max-min)/N. Usually f(x) is periodic
+ * such that f(min) = f(max) (note max is not sampled), but we don't
+ * require that.</p>
+ *
+ * @param f the function to be sampled
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the samples array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public static double[] sample(UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ if (n <= 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_NUMBER_OF_SAMPLES,
+ n);
+ }
+ verifyInterval(min, max);
+
+ double s[] = new double[n];
+ double h = (max - min) / n;
+ for (int i = 0; i < n; i++) {
+ s[i] = f.value(min + i * h);
+ }
+ return s;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns true if the argument is power of 2.
+ *
+ * @param n the number to test
+ * @return true if the argument is power of 2
+ */
+ public static boolean isPowerOf2(long n) {
+ return (n > 0) && ((n & (n - 1)) == 0);
+ }
+
+ /**
+ * Verifies that the data set has length of power of 2.
+ *
+ * @param d the data array
+ * @throws IllegalArgumentException if array length is not power of 2
+ */
+ public static void verifyDataSet(double d[]) throws IllegalArgumentException {
+ if (!isPowerOf2(d.length)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, d.length);
+ }
+ }
+
+ /**
+ * Verifies that the data set has length of power of 2.
+ *
+ * @param o the data array
+ * @throws IllegalArgumentException if array length is not power of 2
+ */
+ public static void verifyDataSet(Object o[]) throws IllegalArgumentException {
+ if (!isPowerOf2(o.length)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, o.length);
+ }
+ }
+
+ /**
+ * Verifies that the endpoints specify an interval.
+ *
+ * @param lower lower endpoint
+ * @param upper upper endpoint
+ * @throws IllegalArgumentException if not interval
+ */
+ public static void verifyInterval(double lower, double upper)
+ throws IllegalArgumentException {
+
+ if (lower >= upper) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper);
+ }
+ }
+
+ /**
+ * Performs a multi-dimensional Fourier transform on a given array.
+ * Use {@link #inversetransform2(Complex[])} and
+ * {@link #transform2(Complex[])} 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>,
+ * n<sub>x</sub>=number of elements in dimension x,
+ * and d=total number of dimensions.
+ *
+ * @param mdca Multi-Dimensional Complex Array id est Complex[][][][]
+ * @param forward inverseTransform2 is preformed if this is false
+ * @return transform of mdca as a Multi-Dimensional Complex Array id est Complex[][][][]
+ * @throws IllegalArgumentException if any dimension is not a power of two
+ */
+ public Object mdfft(Object mdca, boolean forward)
+ throws IllegalArgumentException {
+ 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, forward, i, new int[0]);
+ }
+ return mdcm.getArray();
+ }
+
+ /**
+ * Performs one dimension of a multi-dimensional Fourier transform.
+ *
+ * @param mdcm input matrix
+ * @param forward inverseTransform2 is preformed if this is false
+ * @param d index of the dimension to process
+ * @param subVector recursion subvector
+ * @throws IllegalArgumentException if any dimension is not a power of two
+ */
+ private void mdfft(MultiDimensionalComplexMatrix mdcm, boolean forward,
+ int d, int[] subVector)
+ throws IllegalArgumentException {
+ 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);
+ }
+
+ if (forward)
+ temp = transform2(temp);
+ else
+ temp = inversetransform2(temp);
+
+ 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, forward, d, vector);
+ } else {
+ for (int i = 0; i < dimensionSize[subVector.length]; i++) {
+ vector[subVector.length] = i;
+ //further split along the next dimension
+ mdfft(mdcm, forward, d, vector);
+ }
+ }
+ }
+ return;
+ }
+
+ /**
+ * 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.
+ */
+ 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
+ */
+ public 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 IllegalArgumentException if dimensions do not match
+ */
+ public Complex get(int... vector)
+ throws IllegalArgumentException {
+ if (vector == null) {
+ if (dimensionSize.length > 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, 0, dimensionSize.length);
+ }
+ return null;
+ }
+ if (vector.length != dimensionSize.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, 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 IllegalArgumentException if dimensions do not match
+ */
+ public Complex set(Complex magnitude, int... vector)
+ throws IllegalArgumentException {
+ if (vector == null) {
+ if (dimensionSize.length > 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, 0, dimensionSize.length);
+ }
+ return null;
+ }
+ if (vector.length != dimensionSize.length) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, 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);
+ }
+ }
+ }
+
+
+ /** Computes the n<sup>th</sup> roots of unity.
+ * A cache of already computed values is maintained.
+ */
+ private static class RootsOfUnity implements Serializable {
+
+ /** Serializable version id. */
+ private static final long serialVersionUID = 6404784357747329667L;
+
+ /** Number of roots of unity. */
+ private int omegaCount;
+
+ /** Real part of the roots. */
+ private double[] omegaReal;
+
+ /** Imaginary part of the roots for forward transform. */
+ private double[] omegaImaginaryForward;
+
+ /** Imaginary part of the roots for reverse transform. */
+ private double[] omegaImaginaryInverse;
+
+ /** Forward/reverse indicator. */
+ private boolean isForward;
+
+ /**
+ * Build an engine for computing then <sup>th</sup> roots of unity
+ */
+ public RootsOfUnity() {
+
+ omegaCount = 0;
+ omegaReal = null;
+ omegaImaginaryForward = null;
+ omegaImaginaryInverse = null;
+ isForward = true;
+
+ }
+
+ /**
+ * Check if computation has been done for forward or reverse transform.
+ * @return true if computation has been done for forward transform
+ * @throws IllegalStateException if no roots of unity have been computed yet
+ */
+ public synchronized boolean isForward() throws IllegalStateException {
+
+ if (omegaCount == 0) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ return isForward;
+
+ }
+
+ /** Computes the n<sup>th</sup> roots of unity.
+ * <p>The computed omega[] = { 1, w, w<sup>2</sup>, ... w<sup>(n-1)</sup> } where
+ * w = exp(-2 &pi; i / n), i = &sqrt;(-1).</p>
+ * <p>Note that n is positive for
+ * forward transform and negative for inverse transform.</p>
+ * @param n number of roots of unity to compute,
+ * positive for forward transform, negative for inverse transform
+ * @throws IllegalArgumentException if n = 0
+ */
+ public synchronized void computeOmega(int n) throws IllegalArgumentException {
+
+ if (n == 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY);
+ }
+
+ isForward = n > 0;
+
+ // avoid repetitive calculations
+ final int absN = FastMath.abs(n);
+
+ if (absN == omegaCount) {
+ return;
+ }
+
+ // calculate everything from scratch, for both forward and inverse versions
+ final double t = 2.0 * FastMath.PI / absN;
+ final double cosT = FastMath.cos(t);
+ final double sinT = FastMath.sin(t);
+ omegaReal = new double[absN];
+ omegaImaginaryForward = new double[absN];
+ omegaImaginaryInverse = new double[absN];
+ omegaReal[0] = 1.0;
+ omegaImaginaryForward[0] = 0.0;
+ omegaImaginaryInverse[0] = 0.0;
+ for (int i = 1; i < absN; i++) {
+ omegaReal[i] =
+ omegaReal[i-1] * cosT + omegaImaginaryForward[i-1] * sinT;
+ omegaImaginaryForward[i] =
+ omegaImaginaryForward[i-1] * cosT - omegaReal[i-1] * sinT;
+ omegaImaginaryInverse[i] = -omegaImaginaryForward[i];
+ }
+ omegaCount = absN;
+
+ }
+
+ /**
+ * Get the real part of the k<sup>th</sup> n<sup>th</sup> root of unity
+ * @param k index of the n<sup>th</sup> root of unity
+ * @return real part of the k<sup>th</sup> n<sup>th</sup> root of unity
+ * @throws IllegalStateException if no roots of unity have been computed yet
+ * @throws IllegalArgumentException if k is out of range
+ */
+ public synchronized double getOmegaReal(int k)
+ throws IllegalStateException, IllegalArgumentException {
+
+ if (omegaCount == 0) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ if ((k < 0) || (k >= omegaCount)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, k, 0, omegaCount - 1);
+ }
+
+ return omegaReal[k];
+
+ }
+
+ /**
+ * Get the imaginary part of the k<sup>th</sup> n<sup>th</sup> root of unity
+ * @param k index of the n<sup>th</sup> root of unity
+ * @return imaginary part of the k<sup>th</sup> n<sup>th</sup> root of unity
+ * @throws IllegalStateException if no roots of unity have been computed yet
+ * @throws IllegalArgumentException if k is out of range
+ */
+ public synchronized double getOmegaImaginary(int k)
+ throws IllegalStateException, IllegalArgumentException {
+
+ if (omegaCount == 0) {
+ throw MathRuntimeException.createIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ if ((k < 0) || (k >= omegaCount)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, k, 0, omegaCount - 1);
+ }
+
+ return isForward ? omegaImaginaryForward[k] : omegaImaginaryInverse[k];
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/transform/FastHadamardTransformer.java b/src/main/java/org/apache/commons/math/transform/FastHadamardTransformer.java
new file mode 100644
index 0000000..db79c99
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/FastHadamardTransformer.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.transform;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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).</p>
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public class FastHadamardTransformer implements RealTransformer {
+
+ /** {@inheritDoc} */
+ public double[] transform(double f[])
+ throws IllegalArgumentException {
+ return fht(f);
+ }
+
+ /** {@inheritDoc} */
+ public double[] transform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ return fht(FastFourierTransformer.sample(f, min, max, n));
+ }
+
+ /** {@inheritDoc} */
+ public double[] inversetransform(double f[])
+ throws IllegalArgumentException {
+ return FastFourierTransformer.scaleArray(fht(f), 1.0 / f.length);
+ }
+
+ /** {@inheritDoc} */
+ public double[] inversetransform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+ final double[] unscaled =
+ fht(FastFourierTransformer.sample(f, min, max, n));
+ return FastFourierTransformer.scaleArray(unscaled, 1.0 / n);
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>The integer transform cannot be inverted directly, due to a scaling
+ * factor it may lead to double results.</p>
+ * @param f the integer data array to be transformed (signal)
+ * @return the integer transformed array (spectrum)
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public int[] transform(int f[])
+ throws IllegalArgumentException {
+ return fht(f);
+ }
+
+ /**
+ * The FHT (Fast Hadamard Transformation) which uses only subtraction and addition.
+ * <br>
+ * Requires <b>Nlog2N = n2</b><sup>n</sup> additions.
+ * <br>
+ * <br>
+ * <b><u>Short Table of manual calculation for N=8:</u></b>
+ * <ol>
+ * <li><b>x</b> is the input vector we want to transform</li>
+ * <li><b>y</b> is the output vector which is our desired result</li>
+ * <li>a and b are just helper rows</li>
+ * </ol>
+ * <pre>
+ * <code>
+ * +----+----------+---------+----------+
+ * | <b>x</b> | <b>a</b> | <b>b</b> | <b>y</b> |
+ * +----+----------+---------+----------+
+ * | x<sub>0</sub> | a<sub>0</sub>=x<sub>0</sub>+x<sub>1</sub> | b<sub>0</sub>=a<sub>0</sub>+a<sub>1</sub> | y<sub>0</sub>=b<sub>0</sub>+b<sub>1</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>1</sub> | a<sub>1</sub>=x<sub>2</sub>+x<sub>3</sub> | b<sub>0</sub>=a<sub>2</sub>+a<sub>3</sub> | y<sub>0</sub>=b<sub>2</sub>+b<sub>3</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>2</sub> | a<sub>2</sub>=x<sub>4</sub>+x<sub>5</sub> | b<sub>0</sub>=a<sub>4</sub>+a<sub>5</sub> | y<sub>0</sub>=b<sub>4</sub>+b<sub>5</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>3</sub> | a<sub>3</sub>=x<sub>6</sub>+x<sub>7</sub> | b<sub>0</sub>=a<sub>6</sub>+a<sub>7</sub> | y<sub>0</sub>=b<sub>6</sub>+b<sub>7</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>4</sub> | a<sub>0</sub>=x<sub>0</sub>-x<sub>1</sub> | b<sub>0</sub>=a<sub>0</sub>-a<sub>1</sub> | y<sub>0</sub>=b<sub>0</sub>-b<sub>1</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>5</sub> | a<sub>1</sub>=x<sub>2</sub>-x<sub>3</sub> | b<sub>0</sub>=a<sub>2</sub>-a<sub>3</sub> | y<sub>0</sub>=b<sub>2</sub>-b<sub>3</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>6</sub> | a<sub>2</sub>=x<sub>4</sub>-x<sub>5</sub> | b<sub>0</sub>=a<sub>4</sub>-a<sub>5</sub> | y<sub>0</sub>=b<sub>4</sub>-b<sub>5</sub> |
+ * +----+----------+---------+----------+
+ * | x<sub>7</sub> | a<sub>3</sub>=x<sub>6</sub>-x<sub>7</sub> | b<sub>0</sub>=a<sub>6</sub>-a<sub>7</sub> | y<sub>0</sub>=b<sub>6</sub>-b<sub>7</sub> |
+ * +----+----------+---------+----------+
+ * </code>
+ * </pre>
+ *
+ * <b><u>How it works</u></b>
+ * <ol>
+ * <li>Construct a matrix with N rows and n+1 columns<br> <b>hadm[n+1][N]</b>
+ * <br><i>(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][m])</i></li>
+ * <li>Place the input vector <b>x[N]</b> in the first column of the matrix <b>hadm</b></li>
+ * <li>The entries of the submatrix D<sub>top</sub> are calculated as follows.
+ * <br>D<sub>top</sub> goes from entry [0][1] to [N/2-1][n+1].
+ * <br>The columns of D<sub>top</sub> are the pairwise mutually exclusive sums of the previous column
+ * </li>
+ * <li>The entries of the submatrix D<sub>bottom</sub> are calculated as follows.
+ * <br>D<sub>bottom</sub> goes from entry [N/2][1] to [N][n+1].
+ * <br>The columns of D<sub>bottom</sub> are the pairwise differences of the previous column
+ * </li>
+ * <li>How D<sub>top</sub> and D<sub>bottom</sub> you can understand best with the example for N=8 above.
+ * <li>The output vector y is now in the last column of <b>hadm</b></li>
+ * <li><i>Algorithm from: http://www.archive.chipcenter.com/dsp/DSP000517F1.html</i></li>
+ * </ol>
+ * <br>
+ * <b><u>Visually</u></b>
+ * <pre>
+ * +--------+---+---+---+-----+---+
+ * | 0 | 1 | 2 | 3 | ... |n+1|
+ * +------+--------+---+---+---+-----+---+
+ * |0 | x<sub>0</sub> | /\ |
+ * |1 | x<sub>1</sub> | || |
+ * |2 | x<sub>2</sub> | <= D<sub>top</sub> => |
+ * |... | ... | || |
+ * |N/2-1 | x<sub>N/2-1</sub> | \/ |
+ * +------+--------+---+---+---+-----+---+
+ * |N/2 | x<sub>N/2</sub> | /\ |
+ * |N/2+1 | x<sub>N/2+1</sub> | || |
+ * |N/2+2 | x<sub>N/2+2</sub> | <= D<sub>bottom</sub> => | which is in the last column of the matrix
+ * |... | ... | || |
+ * |N | x<sub>N/2</sub> | \/ |
+ * +------+--------+---+---+---+-----+---+
+ * </pre>
+ *
+ * @param x input vector
+ * @return y output vector
+ * @exception IllegalArgumentException if input array is not a power of 2
+ */
+ protected double[] fht(double x[]) throws IllegalArgumentException {
+
+ // n is the row count of the input vector x
+ final int n = x.length;
+ final int halfN = n / 2;
+
+ // n has to be of the form n = 2^p !!
+ if (!FastFourierTransformer.isPowerOf2(n)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO,
+ n);
+ }
+
+ // Instead of creating a matrix with p+1 columns and n rows
+ // we will use two single dimension arrays which we will use 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) {
+ // D<sub>top</sub>
+ // 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) {
+ // D<sub>bottom</sub>
+ // 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;
+
+ }
+ /**
+ * The FHT (Fast Hadamard Transformation) which uses only subtraction and addition.
+ * @param x input vector
+ * @return y output vector
+ * @exception IllegalArgumentException if input array is not a power of 2
+ */
+ protected int[] fht(int x[]) throws IllegalArgumentException {
+
+ // n is the row count of the input vector x
+ final int n = x.length;
+ final int halfN = n / 2;
+
+ // n has to be of the form n = 2^p !!
+ if (!FastFourierTransformer.isPowerOf2(n)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO,
+ n);
+ }
+
+ // Instead of creating a matrix with p+1 columns and n rows
+ // we will use two single dimension arrays which we will use 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) {
+ // D<sub>top</sub>
+ // 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) {
+ // D<sub>bottom</sub>
+ // 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/math/transform/FastSineTransformer.java b/src/main/java/org/apache/commons/math/transform/FastSineTransformer.java
new file mode 100644
index 0000000..28d7fce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/FastSineTransformer.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.transform;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Implements the <a href="http://documents.wolfram.com/v5/Add-onsLinks/
+ * StandardPackages/LinearAlgebra/FourierTrig.html">Fast Sine Transform</a>
+ * for transformation of one-dimensional data sets. For reference, see
+ * <b>Fast Fourier Transforms</b>, ISBN 0849371635, chapter 3.
+ * <p>
+ * FST is its own inverse, up to a multiplier depending on conventions.
+ * The equations are listed in the comments of the corresponding methods.</p>
+ * <p>
+ * Similar to FFT, we also require the length of data set to be power of 2.
+ * In addition, the first element must be 0 and it's enforced in function
+ * transformation after sampling.</p>
+ * <p>As of version 2.0 this no longer implements Serializable</p>
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 1.2
+ */
+public class FastSineTransformer implements RealTransformer {
+
+ /**
+ * Construct a default transformer.
+ */
+ public FastSineTransformer() {
+ super();
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is F<sub>n</sub> = &sum;<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform(double f[])
+ throws IllegalArgumentException {
+ return fst(f);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is F<sub>n</sub> = &sum;<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform(UnivariateRealFunction f,
+ double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ data[0] = 0.0;
+ return fst(data);
+ }
+
+ /**
+ * Transform the given real data set.
+ * <p>
+ * The formula is F<sub>n</sub> = &radic;(2/N) &sum;<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform2(double f[]) throws IllegalArgumentException {
+
+ double scaling_coefficient = FastMath.sqrt(2.0 / f.length);
+ return FastFourierTransformer.scaleArray(fst(f), scaling_coefficient);
+ }
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is F<sub>n</sub> = &radic;(2/N) &sum;<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated
+ * at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] transform2(
+ UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ data[0] = 0.0;
+ double scaling_coefficient = FastMath.sqrt(2.0 / n);
+ return FastFourierTransformer.scaleArray(fst(data), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is f<sub>k</sub> = (2/N) &sum;<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the real inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform(double f[]) throws IllegalArgumentException {
+
+ double scaling_coefficient = 2.0 / f.length;
+ return FastFourierTransformer.scaleArray(fst(f), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is f<sub>k</sub> = (2/N) &sum;<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform(UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ double data[] = FastFourierTransformer.sample(f, min, max, n);
+ data[0] = 0.0;
+ double scaling_coefficient = 2.0 / n;
+ return FastFourierTransformer.scaleArray(fst(data), scaling_coefficient);
+ }
+
+ /**
+ * Inversely transform the given real data set.
+ * <p>
+ * The formula is f<sub>k</sub> = &radic;(2/N) &sum;<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the real data array to be inversely transformed
+ * @return the real inversely transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform2(double f[]) throws IllegalArgumentException {
+
+ return transform2(f);
+ }
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * <p>
+ * The formula is f<sub>k</sub> = &radic;(2/N) &sum;<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(&pi; nk/N)
+ * </p>
+ *
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ public double[] inversetransform2(UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ return transform2(f, min, max, n);
+ }
+
+ /**
+ * Perform the FST algorithm (including inverse).
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ protected double[] fst(double f[]) throws IllegalArgumentException {
+
+ final double transformed[] = new double[f.length];
+
+ FastFourierTransformer.verifyDataSet(f);
+ if (f[0] != 0.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.FIRST_ELEMENT_NOT_ZERO,
+ 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 = new FastFourierTransformer();
+ Complex y[] = transformer.transform(x);
+
+ // 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/math/transform/RealTransformer.java b/src/main/java/org/apache/commons/math/transform/RealTransformer.java
new file mode 100644
index 0000000..c91061b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/RealTransformer.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.math.transform;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+/**
+ * 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.math.complex.Complex complex}
+ * results instead of real ones.
+ * </p>
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ * @since 2.0
+ */
+public interface RealTransformer {
+
+ /**
+ * Transform the given real data set.
+ * @param f the real data array to be transformed (signal)
+ * @return the real transformed array (spectrum)
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ double[] transform(double f[])
+ throws IllegalArgumentException;
+
+ /**
+ * Transform the given real function, sampled on the given interval.
+ * @param f the function to be sampled and transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ double[] transform(UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+ /**
+ * Inversely transform the given real data set.
+ * @param f the real data array to be inversely transformed (spectrum)
+ * @return the real inversely transformed array (signal)
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ double[] inversetransform(double f[])
+ throws IllegalArgumentException;
+
+ /**
+ * Inversely transform the given real function, sampled on the given interval.
+ * @param f the function to be sampled and inversely transformed
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @param n the number of sample points
+ * @return the real inversely transformed array
+ * @throws FunctionEvaluationException if function cannot be evaluated at some point
+ * @throws IllegalArgumentException if any parameters are invalid
+ */
+ double[] inversetransform(UnivariateRealFunction f, double min, double max, int n)
+ throws FunctionEvaluationException, IllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math/transform/package.html b/src/main/java/org/apache/commons/math/transform/package.html
new file mode 100644
index 0000000..7377906
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/transform/package.html
@@ -0,0 +1,22 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 618423 $ $Date: 2008-02-04 21:29:08 +0100 (lun. 04 févr. 2008) $ -->
+ <body>
+ Implementations of transform methods, including Fast Fourier transforms.
+ </body>
+</html>
diff --git a/src/main/java/org/apache/commons/math/util/BigReal.java b/src/main/java/org/apache/commons/math/util/BigReal.java
new file mode 100644
index 0000000..e7789a4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/BigReal.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import java.math.RoundingMode;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+
+/**
+ * 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.
+ * </p>
+ * @since 2.0
+ * @version $Revision: 925812 $ $Date: 2010-03-21 16:49:31 +0100 (dim. 21 mars 2010) $
+ */
+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 divide(BigReal a) throws ArithmeticException {
+ return new BigReal(d.divide(a.d, scale, roundingMode));
+ }
+
+ /** {@inheritDoc} */
+ public BigReal multiply(BigReal a) {
+ return new BigReal(d.multiply(a.d));
+ }
+
+ /** {@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/math/util/BigRealField.java b/src/main/java/org/apache/commons/math/util/BigRealField.java
new file mode 100644
index 0000000..02361bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/BigRealField.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.math.util;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.Field;
+
+/**
+ * Representation of real numbers with arbitrary precision field.
+ * <p>
+ * This class is a singleton.
+ * </p>
+ * @see BigReal
+ * @version $Revision: 811827 $ $Date: 2009-09-06 17:32:50 +0200 (dim. 06 sept. 2009) $
+ * @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;
+ }
+
+ // 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 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/math/util/CompositeFormat.java b/src/main/java/org/apache/commons/math/util/CompositeFormat.java
new file mode 100644
index 0000000..99d18ab
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/CompositeFormat.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.math.util;
+
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Base class for formatters of composite objects (complex numbers, vectors ...).
+ *
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+public abstract class CompositeFormat extends Format {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 5358685519349262494L;
+
+ /**
+ * 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 2.
+ * @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#getInstance(java.util.Locale)} with the only
+ * customizing that the maximum number of fraction digits is set to 2.
+ * @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.getInstance(locale);
+ nf.setMaximumFractionDigits(2);
+ return nf;
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter. On output, <code>pos</code>
+ * holds the index of the next non-whitespace character.
+ */
+ protected 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/ouput parsing parameter.
+ * @return the first non-whitespace character.
+ */
+ protected 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/ouput parsing parameter.
+ * @return the special number.
+ */
+ private 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()) {
+ if (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/ouput parsing parameter.
+ * @return the parsed number.
+ */
+ protected 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/ouput parsing parameter.
+ * @return true if the expected string was there
+ */
+ protected 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>
+ * <li>Positive infinity is formatted as '(Infinity)'</li>
+ * <li>Negative infinity is formatted as '(-Infinity)'</li>
+ * </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.
+ */
+ protected 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/math/util/ContinuedFraction.java b/src/main/java/org/apache/commons/math/util/ContinuedFraction.java
new file mode 100644
index 0000000..80df5d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/ContinuedFraction.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.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></li>
+ * </ul>
+ * </p>
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+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 MathException if the algorithm fails to converge.
+ */
+ public double evaluate(double x) throws MathException {
+ 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 MathException if the algorithm fails to converge.
+ */
+ public double evaluate(double x, double epsilon) throws MathException {
+ 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 MathException if the algorithm fails to converge.
+ */
+ public double evaluate(double x, int maxIterations) throws MathException {
+ return evaluate(x, DEFAULT_EPSILON, maxIterations);
+ }
+
+ /**
+ * <p>
+ * Evaluates the continued fraction at the value x.
+ * </p>
+ *
+ * <p>
+ * The implementation of this method is based on equations 14-17 of:
+ * <ul>
+ * <li>
+ * Eric W. Weisstein. "Continued Fraction." From MathWorld--A Wolfram Web
+ * Resource. <a target="_blank"
+ * href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ * http://mathworld.wolfram.com/ContinuedFraction.html</a>
+ * </li>
+ * </ul>
+ * The recurrence relationship defined in those equations can result in
+ * very large intermediate results which can result in numerical overflow.
+ * As a means to combat these overflow conditions, the intermediate results
+ * are scaled whenever they threaten to become numerically unstable.</p>
+ *
+ * @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 MathException if the algorithm fails to converge.
+ */
+ public double evaluate(double x, double epsilon, int maxIterations)
+ throws MathException
+ {
+ double p0 = 1.0;
+ double p1 = getA(0, x);
+ double q0 = 0.0;
+ double q1 = 1.0;
+ double c = p1 / q1;
+ int n = 0;
+ double relativeError = Double.MAX_VALUE;
+ while (n < maxIterations && relativeError > epsilon) {
+ ++n;
+ double a = getA(n, x);
+ double b = getB(n, x);
+ double p2 = a * p1 + b * p0;
+ double q2 = a * q1 + b * q0;
+ boolean infinite = false;
+ if (Double.isInfinite(p2) || Double.isInfinite(q2)) {
+ /*
+ * Need to scale. Try successive powers of the larger of a or b
+ * up to 5th power. Throw ConvergenceException if one or both
+ * of p2, q2 still overflow.
+ */
+ double scaleFactor = 1d;
+ double lastScaleFactor = 1d;
+ final int maxPower = 5;
+ final double scale = FastMath.max(a,b);
+ if (scale <= 0) { // Can't scale
+ throw new ConvergenceException(
+ LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE,
+ x);
+ }
+ infinite = true;
+ for (int i = 0; i < maxPower; i++) {
+ lastScaleFactor = scaleFactor;
+ scaleFactor *= scale;
+ if (a != 0.0 && a > b) {
+ p2 = p1 / lastScaleFactor + (b / scaleFactor * p0);
+ q2 = q1 / lastScaleFactor + (b / scaleFactor * q0);
+ } else if (b != 0) {
+ p2 = (a / scaleFactor * p1) + p0 / lastScaleFactor;
+ q2 = (a / scaleFactor * q1) + q0 / lastScaleFactor;
+ }
+ infinite = Double.isInfinite(p2) || Double.isInfinite(q2);
+ if (!infinite) {
+ break;
+ }
+ }
+ }
+
+ if (infinite) {
+ // Scaling failed
+ throw new ConvergenceException(
+ LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE,
+ x);
+ }
+
+ double r = p2 / q2;
+
+ if (Double.isNaN(r)) {
+ throw new ConvergenceException(
+ LocalizedFormats.CONTINUED_FRACTION_NAN_DIVERGENCE,
+ x);
+ }
+ relativeError = FastMath.abs(r / c - 1.0);
+
+ // prepare for next iteration
+ c = p2 / q2;
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ }
+
+ if (n >= maxIterations) {
+ throw new MaxIterationsExceededException(maxIterations,
+ LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION,
+ x);
+ }
+
+ return c;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math/util/DefaultTransformer.java b/src/main/java/org/apache/commons/math/util/DefaultTransformer.java
new file mode 100644
index 0000000..e4579b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/DefaultTransformer.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.math.util;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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.
+ *
+ * @version $Revision: 1073658 $ $Date: 2011-02-23 10:45:42 +0100 (mer. 23 févr. 2011) $
+ */
+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 MathException if it 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 MathException {
+ if (o == null) {
+ throw new MathException(LocalizedFormats.OBJECT_TRANSFORMATION);
+ }
+
+ if (o instanceof Number) {
+ return ((Number)o).doubleValue();
+ }
+
+ try {
+ return Double.valueOf(o.toString()).doubleValue();
+ } catch (NumberFormatException e) {
+ throw new MathException(e,
+ LocalizedFormats.CANNOT_TRANSFORM_TO_DOUBLE, e.getMessage());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ return other instanceof DefaultTransformer;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ // some arbitrary number ...
+ return 401993047;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/DoubleArray.java b/src/main/java/org/apache/commons/math/util/DoubleArray.java
new file mode 100644
index 0000000..c213b84
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/DoubleArray.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.math.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".
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+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);
+
+ /**
+ * <p>
+ * 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>
+ * <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.
+ * </p>
+ *
+ * @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/math/util/FastMath.java b/src/main/java/org/apache/commons/math/util/FastMath.java
new file mode 100644
index 0000000..1907b32
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/FastMath.java
@@ -0,0 +1,4047 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+/**
+ * Faster, more accurate, portable alternative to {@link StrictMath}.
+ * <p>
+ * Additionally implements the following methods not found in StrictMath:
+ * <ul>
+ * <li>{@link #asinh(double)}</li>
+ * <li>{@link #acosh(double)}</li>
+ * <li>{@link #atanh(double)}</li>
+ * </ul>
+ * The following methods are found in StrictMath since 1.6 only
+ * <ul>
+ * <li>{@link #copySign(double, double)}</li>
+ * <li>{@link #getExponent(double)}</li>
+ * <li>{@link #nextAfter(double,double)}</li>
+ * <li>{@link #nextUp(double)}</li>
+ * <li>{@link #scalb(double, int)}</li>
+ * <li>{@link #copySign(float, float)}</li>
+ * <li>{@link #getExponent(float)}</li>
+ * <li>{@link #nextAfter(float,double)}</li>
+ * <li>{@link #nextUp(float)}</li>
+ * <li>{@link #scalb(float, int)}</li>
+ * </ul>
+ * @version $Revision: 1074294 $ $Date: 2011-02-24 22:18:59 +0100 (jeu. 24 févr. 2011) $
+ * @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;
+
+ /** Exponential evaluated at integer values,
+ * exp(x) = expIntTableA[x + 750] + expIntTableB[x+750].
+ */
+ private static final double EXP_INT_TABLE_A[] = new double[1500];
+
+ /** Exponential evaluated at integer values,
+ * exp(x) = expIntTableA[x + 750] + expIntTableB[x+750]
+ */
+ private static final double EXP_INT_TABLE_B[] = new double[1500];
+
+ /** 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_A[] = new double[1025];
+
+ /** 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[] = new double[1025];
+
+ /** Factorial table, for Taylor series expansions. */
+ private static final double FACT[] = new double[20];
+
+ /** Extended precision logarithm table over the range 1 - 2 in increments of 2^-10. */
+ private static final double LN_MANT[][] = new double[1024][];
+
+ /** 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 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},
+ };
+
+ /** 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 table (high bits). */
+ private static final double SINE_TABLE_A[] = new double[14];
+
+ /** Sine table (low bits). */
+ private static final double SINE_TABLE_B[] = new double[14];
+
+ /** Cosine table (high bits). */
+ private static final double COSINE_TABLE_A[] = new double[14];
+
+ /** Cosine table (low bits). */
+ private static final double COSINE_TABLE_B[] = new double[14];
+
+ /** Tangent table, used by atan() (high bits). */
+ private static final double TANGENT_TABLE_A[] = new double[14];
+
+ /** Tangent table, used by atan() (low bits). */
+ private static final double TANGENT_TABLE_B[] = new double[14];
+
+ /** 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;
+
+ /** 2^52 - double numbers this large must be integral (no fraction) or NaN or Infinite */
+ private static final double TWO_POWER_52 = 4503599627370496.0;
+
+ // Initialize tables
+ static {
+ int i;
+
+ // Generate an array of factorials
+ FACT[0] = 1.0;
+ for (i = 1; i < FACT.length; i++) {
+ FACT[i] = FACT[i-1] * i;
+ }
+
+ double tmp[] = new double[2];
+ double recip[] = new double[2];
+
+ // Populate expIntTable
+ for (i = 0; i < 750; i++) {
+ expint(i, tmp);
+ EXP_INT_TABLE_A[i+750] = tmp[0];
+ EXP_INT_TABLE_B[i+750] = tmp[1];
+
+ if (i != 0) {
+ // Negative integer powers
+ splitReciprocal(tmp, recip);
+ EXP_INT_TABLE_A[750-i] = recip[0];
+ EXP_INT_TABLE_B[750-i] = recip[1];
+ }
+ }
+
+ // Populate expFracTable
+ for (i = 0; i < EXP_FRAC_TABLE_A.length; i++) {
+ slowexp(i/1024.0, tmp);
+ EXP_FRAC_TABLE_A[i] = tmp[0];
+ EXP_FRAC_TABLE_B[i] = tmp[1];
+ }
+
+ // Populate lnMant table
+ for (i = 0; i < LN_MANT.length; i++) {
+ double d = Double.longBitsToDouble( (((long) i) << 42) | 0x3ff0000000000000L );
+ LN_MANT[i] = slowLog(d);
+ }
+
+ // Build the sine and cosine tables
+ buildSinCosTables();
+ }
+
+ /**
+ * 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 > -MathUtils.SAFE_MIN && d < MathUtils.SAFE_MIN){
+ return d; // These are un-normalised - don't try to convert
+ }
+ long xl = Double.doubleToLongBits(d);
+ xl = 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;
+ }
+
+ if (x > 20.0) {
+ return exp(x)/2.0;
+ }
+
+ if (x < -20) {
+ return exp(-x)/2.0;
+ }
+
+ 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;
+ }
+
+ if (x > 20.0) {
+ return exp(x)/2.0;
+ }
+
+ if (x < -20) {
+ return -exp(-x)/2.0;
+ }
+
+ 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;
+ }
+
+ 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 * (1 / 3.0 - a2 * (1 / 5.0 - a2 * (1 / 7.0 - a2 * (1 / 9.0 - a2 * (1.0 / 11.0 - a2 * (1.0 / 13.0 - a2 * (1.0 / 15.0 - a2 * (1.0 / 17.0) * 15.0 / 16.0) * 13.0 / 14.0) * 11.0 / 12.0) * 9.0 / 10.0) * 7.0 / 8.0) * 5.0 / 6.0) * 3.0 / 4.0) / 2.0);
+ } else if (a > 0.036) {
+ absAsinh = a * (1 - a2 * (1 / 3.0 - a2 * (1 / 5.0 - a2 * (1 / 7.0 - a2 * (1 / 9.0 - a2 * (1.0 / 11.0 - a2 * (1.0 / 13.0) * 11.0 / 12.0) * 9.0 / 10.0) * 7.0 / 8.0) * 5.0 / 6.0) * 3.0 / 4.0) / 2.0);
+ } else if (a > 0.0036) {
+ absAsinh = a * (1 - a2 * (1 / 3.0 - a2 * (1 / 5.0 - a2 * (1 / 7.0 - a2 * (1 / 9.0) * 7.0 / 8.0) * 5.0 / 6.0) * 3.0 / 4.0) / 2.0);
+ } else {
+ absAsinh = a * (1 - a2 * (1 / 3.0 - a2 * (1 / 5.0) * 3.0 / 4.0) / 2.0);
+ }
+ }
+
+ 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 * (1.0 / 3.0 + a2 * (1.0 / 5.0 + a2 * (1.0 / 7.0 + a2 * (1.0 / 9.0 + a2 * (1.0 / 11.0 + a2 * (1.0 / 13.0 + a2 * (1.0 / 15.0 + a2 * (1.0 / 17.0)))))))));
+ } else if (a > 0.031) {
+ absAtanh = a * (1 + a2 * (1.0 / 3.0 + a2 * (1.0 / 5.0 + a2 * (1.0 / 7.0 + a2 * (1.0 / 9.0 + a2 * (1.0 / 11.0 + a2 * (1.0 / 13.0)))))));
+ } else if (a > 0.003) {
+ absAtanh = a * (1 + a2 * (1.0 / 3.0 + a2 * (1.0 / 5.0 + a2 * (1.0 / 7.0 + a2 * (1.0 / 9.0)))));
+ } else {
+ absAtanh = a * (1 + a2 * (1.0 / 3.0 + a2 * (1.0 / 5.0)));
+ }
+ }
+
+ 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);
+ }
+
+ /** 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.
+ *
+ * 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 UPL error.
+ *
+ * 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)
+ *
+ * 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;
+
+ /* Lookup exp(floor(x)).
+ * intPartA will have the upper 22 bits, intPartB will have the lower
+ * 52 bits.
+ */
+ if (x < 0.0) {
+ intVal = (int) -x;
+
+ if (intVal > 746) {
+ 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++;
+
+ intPartA = EXP_INT_TABLE_A[750-intVal];
+ intPartB = EXP_INT_TABLE_B[750-intVal];
+
+ intVal = -intVal;
+ } else {
+ intVal = (int) x;
+
+ if (intVal > 709) {
+ if (hiPrec != null) {
+ hiPrec[0] = Double.POSITIVE_INFINITY;
+ hiPrec[1] = 0.0;
+ }
+ return Double.POSITIVE_INFINITY;
+ }
+
+ intPartA = EXP_INT_TABLE_A[750+intVal];
+ intPartB = EXP_INT_TABLE_B[750+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 = EXP_FRAC_TABLE_A[intFrac];
+ final double fracPartB = 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 percison.
+ */
+ 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 contant 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;
+ 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 = EXP_FRAC_TABLE_A[intFrac] - 1.0;
+ double tempB = 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 = zb * epsilon;
+ zb = 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;
+ }
+
+ /**
+ * 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)
+ */
+ private 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 = 19; 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) {
+ 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.
+ *
+ * Set b = 1/(2^22), a = 1 - b. Thus (a+b) = 1.
+ * Use following identity to compute (a+b)/(c+d)
+ *
+ * (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
+ */
+ private 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 = 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] = result[1] - (tmp - result[0] - zs[0]);
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[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] = result[1] - (tmp - result[0] - zs[0]);
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[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] = result[1] - (tmp - result[0] - zs[0]);
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[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])
+ */
+ private 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];
+ }
+
+
+ /**
+ * 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.doubleToLongBits(x);
+
+ /* Handle special cases of negative input, and NaN */
+ if ((bits & 0x8000000000000000L) != 0 || x != x) {
+ if (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) {
+ if (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;
+
+ double ya = LN_QUICK_COEF[LN_QUICK_COEF.length-1][0];
+ double yb = LN_QUICK_COEF[LN_QUICK_COEF.length-1][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 */
+ aa = ya + LN_QUICK_COEF[i][0];
+ ab = yb + LN_QUICK_COEF[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)
+ double lnm[] = 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;
+ 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. */
+ double numer = bits & 0x3ffffffffffL;
+ double denom = TWO_POWER_52 + (bits & 0x000ffc0000000000L);
+ aa = numer - xa*denom - xb * denom;
+ xb += aa / denom;
+
+ /* Remez polynomial evaluation */
+ double ya = LN_HI_PREC_COEF[LN_HI_PREC_COEF.length-1][0];
+ double yb = LN_HI_PREC_COEF[LN_HI_PREC_COEF.length-1][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 */
+ aa = ya + LN_HI_PREC_COEF[i][0];
+ ab = yb + LN_HI_PREC_COEF[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 = 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 = b + d;
+
+ c = a + lnza;
+ d = -(c - a - lnza);
+ a = c;
+ b = b + d;
+
+ c = a + LN_2_B*exp;
+ d = -(c - a - LN_2_B*exp);
+ a = c;
+ b = b + d;
+
+ c = a + lnm[1];
+ d = -(c - a - lnm[1]);
+ a = c;
+ b = b + d;
+
+ c = a + lnzb;
+ d = -(c - a - lnzb);
+ a = c;
+ b = b + d;
+
+ if (hiPrec != null) {
+ hiPrec[0] = a;
+ hiPrec[1] = b;
+ }
+
+ return a + b;
+ }
+
+ /** Compute log(1 + x).
+ * @param x a number
+ * @return log(1 + x)
+ */
+ public static double log1p(final double x) {
+ double xpa = 1.0 + x;
+ double xpb = -(xpa - 1.0 - x);
+
+ if (x == -1) {
+ return x/0.0; // -Infinity
+ }
+
+ if (x > 0 && 1/x == 0) { // x = Infinity
+ return x;
+ }
+
+ if (x>1e-6 || x<-1e-6) {
+ 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 */
+ double fx1 = xpb/xpa;
+
+ double epsilon = 0.5 * fx1 + 1.0;
+ epsilon = epsilon * fx1;
+
+ return epsilon + hiPrec[1] + hiPrec[0];
+ }
+
+ /* Value is small |x| < 1e6, do a Taylor series centered on 1.0 */
+ double y = x * 0.333333333333333 - 0.5;
+ y = y * x + 1.0;
+ y = y * x;
+
+ return y;
+ }
+
+ /** 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;
+ }
+
+ /**
+ * Power function. Compute x^y.
+ *
+ * @param x a double
+ * @param y a double
+ * @return double
+ */
+ public static double pow(double x, double y) {
+ final double lns[] = new double[2];
+
+ if (y == 0.0) {
+ return 1.0;
+ }
+
+ if (x != x) { // X is NaN
+ return x;
+ }
+
+
+ if (x == 0) {
+ long bits = Double.doubleToLongBits(x);
+ if ((bits & 0x8000000000000000L) != 0) {
+ // -zero
+ long yi = (long) y;
+
+ if (y < 0 && y == yi && (yi & 1) == 1) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ if (y < 0 && y == yi && (yi & 1) == 1) {
+ return -0.0;
+ }
+
+ if (y > 0 && y == yi && (yi & 1) == 1) {
+ return -0.0;
+ }
+ }
+
+ if (y < 0) {
+ return Double.POSITIVE_INFINITY;
+ }
+ if (y > 0) {
+ return 0.0;
+ }
+
+ return Double.NaN;
+ }
+
+ if (x == Double.POSITIVE_INFINITY) {
+ if (y != y) { // y is NaN
+ return y;
+ }
+ if (y < 0.0) {
+ return 0.0;
+ } else {
+ return Double.POSITIVE_INFINITY;
+ }
+ }
+
+ if (y == Double.POSITIVE_INFINITY) {
+ if (x * x == 1.0)
+ return Double.NaN;
+
+ if (x * x > 1.0) {
+ return Double.POSITIVE_INFINITY;
+ } else {
+ return 0.0;
+ }
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ if (y != y) { // y is NaN
+ return y;
+ }
+
+ if (y < 0) {
+ long yi = (long) y;
+ if (y == yi && (yi & 1) == 1) {
+ return -0.0;
+ }
+
+ return 0.0;
+ }
+
+ if (y > 0) {
+ long yi = (long) y;
+ if (y == yi && (yi & 1) == 1) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ return Double.POSITIVE_INFINITY;
+ }
+ }
+
+ if (y == Double.NEGATIVE_INFINITY) {
+
+ if (x * x == 1.0) {
+ return Double.NaN;
+ }
+
+ if (x * x < 1.0) {
+ return Double.POSITIVE_INFINITY;
+ } else {
+ return 0.0;
+ }
+ }
+
+ /* Handle special case x<0 */
+ if (x < 0) {
+ // y is an even integer in this case
+ if (y >= TWO_POWER_52 || y <= -TWO_POWER_52) {
+ return pow(-x, y);
+ }
+
+ if (y == (long) y) {
+ // If y is an integer
+ return ((long)y & 1) == 0 ? pow(-x, y) : -pow(-x, y);
+ } else {
+ return Double.NaN;
+ }
+ }
+
+ /* Split y into ya and yb such that y = ya+yb */
+ double ya;
+ double yb;
+ if (y < 8e298 && y > -8e298) {
+ double tmp1 = y * HEX_40000000;
+ ya = y + tmp1 - tmp1;
+ yb = y - ya;
+ } else {
+ double tmp1 = y * 9.31322574615478515625E-10;
+ double tmp2 = tmp1 * 9.31322574615478515625E-10;
+ ya = (tmp1 + tmp2 - tmp1) * HEX_40000000 * HEX_40000000;
+ yb = y - ya;
+ }
+
+ /* Compute ln(x) */
+ 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 */
+ double tmp1 = lna * HEX_40000000;
+ 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 = z * lnb;
+
+ final double result = exp(lna, z, null);
+ //result = result + result * z;
+ return result;
+ }
+
+ /** xi in the range of [1, 2].
+ * 3 5 7
+ * x+1 / x x x \
+ * ln ----- = 2 * | x + ---- + ---- + ---- + ... |
+ * 1-x \ 3 5 7 /
+ *
+ * So, compute a Remez approximation of the following function
+ *
+ * ln ((sqrt(x)+1)/(1-sqrt(x))) / x
+ *
+ * This will be an even function with only positive coefficents.
+ * x is in the range [0 - 1/3].
+ *
+ * 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)
+ */
+ private 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;
+ }
+
+ /**
+ * For x between 0 and pi/4 compute sine.
+ * @param x number from which sine is requested
+ * @param result placeholder where to put the result in extended precision
+ * @return sin(x)
+ */
+ private 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 = 19; i >= 0; i--) {
+ splitMult(xs, ys, as);
+ ys[0] = as[0]; ys[1] = as[1];
+
+ if ( (i & 1) == 0) {
+ continue;
+ }
+
+ split(FACT[i], as);
+ splitReciprocal(as, facts);
+
+ if ( (i & 2) != 0 ) {
+ 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 cosine
+ * @param x number from which cosine is requested
+ * @param result placeholder where to put the result in extended precision
+ * @return cos(x)
+ */
+ private 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 = 19; i >= 0; i--) {
+ splitMult(xs, ys, as);
+ ys[0] = as[0]; ys[1] = as[1];
+
+ if ( (i & 1) != 0) {
+ continue;
+ }
+
+ split(FACT[i], as);
+ splitReciprocal(as, facts);
+
+ if ( (i & 2) != 0 ) {
+ 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];
+ }
+
+ /** Build the sine and cosine tables.
+ */
+ private static void buildSinCosTables() {
+ 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 < 14; 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 < 14; 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];
+ }
+
+ }
+
+ /**
+ * 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 = b + d;
+
+ t = costA * sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = 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 = 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 = b + d;
+
+ t = costA*sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ b = b + sintA*cosEpsB + costA*sinEpsB;
+ b = 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 = b + d;
+
+ t = -sintA*sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ b = b + costB*cosEpsA + costA*cosEpsB + costB*cosEpsB;
+ b = 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.doubleToLongBits(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 = prodB + (bc << 32);
+ prodA = 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 = 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 = prod2B + (bc << 32);
+ prod2A = 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 = 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 = 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 a number
+ * @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.doubleToLongBits(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) {
+ /* Inline the Cody/Waite reduction for performance */
+
+ // 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.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--;
+ }
+ quadrant = k & 3;
+ xa = remA;
+ xb = remB;
+ }
+
+ 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 a number
+ * @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) {
+ /* Inline the Cody/Waite reduction for performance */
+
+ // 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.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--;
+ }
+ quadrant = k & 3;
+ xa = remA;
+ xb = remB;
+ }
+
+ //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 a number
+ * @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.doubleToLongBits(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) {
+ /* Inline the Cody/Waite reduction for performance */
+
+ // 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.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--;
+ }
+ quadrant = k & 3;
+ xa = remA;
+ xb = remB;
+ }
+
+ if (xa > 1.5) {
+ // Accurracy 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) {
+ boolean negate = false;
+ int idx;
+
+ if (xa == 0.0) { // Matches +/- 0.0; return correct sign
+ return leftPlane ? copySign(Math.PI, xa) : xa;
+ }
+
+ if (xa < 0) {
+ // negative
+ xa = -xa;
+ xb = -xb;
+ negate = true;
+ }
+
+ if (xa > 1.633123935319537E16) { // Very large input
+ return (negate ^ leftPlane) ? (-Math.PI/2.0) : (Math.PI/2.0);
+ }
+
+ /* Estimate the closest tabulated arctan value, compute eps = xa-tangentTable */
+ if (xa < 1.0) {
+ idx = (int) (((-1.7168146928204136 * xa * xa + 8.0) * xa) + 0.5);
+ } else {
+ double temp = 1.0/xa;
+ idx = (int) (-((-1.7168146928204136 * temp * temp + 8.0) * temp) + 13.07);
+ }
+ double epsA = xa - TANGENT_TABLE_A[idx];
+ double epsB = -(epsA - xa + TANGENT_TABLE_A[idx]);
+ epsB += xb - TANGENT_TABLE_B[idx];
+
+ 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]);
+ double denom = 1.0 / (1.0 + (xa + xb) * (TANGENT_TABLE_A[idx] + TANGENT_TABLE_B[idx]));
+ //double denom = 1.0 / (1.0 + xa*tangentTableA[idx]);
+ ya = epsA * denom;
+ yb = epsB * denom;
+ } else {
+ double temp2 = xa * TANGENT_TABLE_A[idx];
+ double za = 1.0 + temp2;
+ double zb = -(za - 1.0 - temp2);
+ temp2 = xb * TANGENT_TABLE_A[idx] + xa * TANGENT_TABLE_B[idx];
+ temp = za + temp2;
+ zb += -(temp - za - temp2);
+ za = temp;
+
+ zb += xb * TANGENT_TABLE_B[idx];
+ 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 */
+ 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 / (1.0 + epsA * epsA);
+
+ double result;
+ double resultb;
+
+ //result = yb + eighths[idx] + ya;
+ double za = EIGHTHS[idx] + ya;
+ double zb = -(za - EIGHTHS[idx] - ya);
+ temp = za + yb;
+ zb += -(temp - za - yb);
+ za = temp;
+
+ result = za + zb;
+ resultb = -(result - za - zb);
+
+ if (leftPlane) {
+ // Result is in the left plane
+ final double pia = 1.5707963267948966*2.0;
+ final double pib = 6.123233995736766E-17*2.0;
+
+ za = pia - result;
+ zb = -(za - pia + result);
+ zb += pib - resultb;
+
+ result = za + zb;
+ 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.0) {
+ double result = x*y;
+ double invx = 1.0/x;
+ double invy = 1.0/y;
+
+ if (invx == 0.0) { // X is infinite
+ if (x > 0) {
+ return y; // return +/- 0.0
+ } else {
+ return copySign(Math.PI, y);
+ }
+ }
+
+ if (x < 0.0 || invx < 0.0) {
+ if (y < 0.0 || invy < 0.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/4.0;
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ return Math.PI*3.0/4.0;
+ }
+
+ return Math.PI/2.0;
+ }
+
+ if (y == Double.NEGATIVE_INFINITY) {
+ if (x == Double.POSITIVE_INFINITY) {
+ return -Math.PI/4.0;
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ return -Math.PI*3.0/4.0;
+ }
+
+ return -Math.PI/2.0;
+ }
+
+ if (x == Double.POSITIVE_INFINITY) {
+ if (y > 0.0 || 1/y > 0.0) {
+ return 0.0;
+ }
+
+ if (y < 0.0 || 1/y < 0.0) {
+ return -0.0;
+ }
+ }
+
+ if (x == Double.NEGATIVE_INFINITY)
+ {
+ if (y > 0.0 || 1/y > 0.0) {
+ return Math.PI;
+ }
+
+ if (y < 0.0 || 1/y < 0.0) {
+ return -Math.PI;
+ }
+ }
+
+ // Neither y nor x can be infinite or NAN here
+
+ if (x == 0) {
+ if (y > 0.0 || 1/y > 0.0) {
+ return Math.PI/2.0;
+ }
+
+ if (y < 0.0 || 1/y < 0.0) {
+ return -Math.PI/2.0;
+ }
+ }
+
+ // 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;
+
+ double temp = ra + rb;
+ rb = -(temp - ra - rb);
+ ra = temp;
+
+ if (ra == 0) { // Fix up the sign so atan works correctly
+ ra = copySign(0.0, y);
+ }
+
+ // Call atan
+ 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.doubleToLongBits(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.doubleToLongBits(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 = 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 = 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) {
+ return (x < 0) ? -x : x;
+ }
+
+ /**
+ * Absolute value.
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static long abs(final long x) {
+ return (x < 0l) ? -x : x;
+ }
+
+ /**
+ * Absolute value.
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static float abs(final float x) {
+ return (x < 0.0f) ? -x : (x == 0.0f) ? 0.0f : x; // -0.0 => +0.0
+ }
+
+ /**
+ * Absolute value.
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static double abs(double x) {
+ return (x < 0.0) ? -x : (x == 0.0) ? 0.0 : x; // -0.0 => +0.0
+ }
+
+ /**
+ * 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.doubleToLongBits(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.doubleToLongBits(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 = mantissa | (1L << 52);
+
+ // scales down complete mantissa, hence losing least significant bits
+ final long mostSignificantLostBit = mantissa & (1L << (-scaledExponent));
+ mantissa = 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 = mantissa << 1;
+ --scaledExponent;
+ }
+ ++scaledExponent;
+ mantissa = 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 = mantissa | (1 << 23);
+
+ // scales down complete mantissa, hence losing least significant bits
+ final int mostSignificantLostBit = mantissa & (1 << (-scaledExponent));
+ mantissa = 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 = mantissa << 1;
+ --scaledExponent;
+ }
+ ++scaledExponent;
+ mantissa = 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>
+ * <li>-MAX_VALUE</li>
+ * <li>-MIN_VALUE</li>
+ * <li>-0.0</li>
+ * <li>+0.0</li>
+ * <li>+MIN_VALUE</li>
+ * <li>+MAX_VALUE</li>
+ * <li>+INFINITY</li>
+ * <li></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>
+ * <p>
+ * If {@code d} is infinite and direction does not
+ * bring it back to finite numbers, it is returned unchanged.</p>
+ *
+ * @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
+
+ final long bits = Double.doubleToLongBits(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>
+ * <li>-MAX_VALUE</li>
+ * <li>-MIN_VALUE</li>
+ * <li>-0.0</li>
+ * <li>+0.0</li>
+ * <li>+MIN_VALUE</li>
+ * <li>+MAX_VALUE</li>
+ * <li>+INFINITY</li>
+ * <li></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>
+ * <p>
+ * If {@code f} is infinite and direction does not
+ * bring it back to finite numbers, it is returned unchanged.</p>
+ *
+ * @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>
+ * <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 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>
+ * <li>If the result is not NaN, the sign of the result equals the sign of the dividend.</li>
+ * <li>If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.</li>
+ * <li>If the dividend is finite and the divisor is an infinity, the result equals the dividend.</li>
+ * <li>If the dividend is a zero and the divisor is finite, the result equals the dividend.</li>
+ * </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
+ }
+
+ /**
+ * 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){
+ long m = Double.doubleToLongBits(magnitude);
+ long s = Double.doubleToLongBits(sign);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ 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){
+ int m = Float.floatToIntBits(magnitude);
+ int s = Float.floatToIntBits(sign);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ 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.
+ * </p>
+ * @param d number from which exponent is requested
+ * @return exponent for d in IEEE754 representation, without bias
+ */
+ public static int getExponent(final double d) {
+ return (int) ((Double.doubleToLongBits(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.
+ * </p>
+ * @param f number from which exponent is requested
+ * @return exponent for d in IEEE754 representation, without bias
+ */
+ public static int getExponent(final float f) {
+ return ((Float.floatToIntBits(f) >>> 23) & 0xff) - 127;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/MathUtils.java b/src/main/java/org/apache/commons/math/util/MathUtils.java
new file mode 100644
index 0000000..c1999bd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/MathUtils.java
@@ -0,0 +1,2265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.exception.NonMonotonousSequenceException;
+
+/**
+ * Some useful additions to the built-in functions in {@link Math}.
+ * @version $Revision: 1073472 $ $Date: 2011-02-22 20:49:07 +0100 (mar. 22 févr. 2011) $
+ */
+public final class MathUtils {
+
+ /** Smallest positive number such that 1 - EPSILON is not numerically equal to 1. */
+ public static final double EPSILON = 0x1.0p-53;
+
+ /** Safe minimum, such that 1 / SAFE_MIN does not overflow.
+ * <p>In IEEE 754 arithmetic, this is also the smallest normalized
+ * number 2<sup>-1022</sup>.</p>
+ */
+ public static final double SAFE_MIN = 0x1.0p-1022;
+
+ /**
+ * 2 &pi;.
+ * @since 2.1
+ */
+ public static final double TWO_PI = 2 * FastMath.PI;
+
+ /** -1.0 cast as a byte. */
+ private static final byte NB = (byte)-1;
+
+ /** -1.0 cast as a short. */
+ private static final short NS = (short)-1;
+
+ /** 1.0 cast as a byte. */
+ private static final byte PB = (byte)1;
+
+ /** 1.0 cast as a short. */
+ private static final short PS = (short)1;
+
+ /** 0.0 cast as a byte. */
+ private static final byte ZB = (byte)0;
+
+ /** 0.0 cast as a short. */
+ private static final short ZS = (short)0;
+
+ /** Gap between NaN and regular numbers. */
+ private static final int NAN_GAP = 4 * 1024 * 1024;
+
+ /** 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;
+
+ /** All long-representable factorials */
+ private 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 };
+
+ /**
+ * Private Constructor
+ */
+ private MathUtils() {
+ super();
+ }
+
+ /**
+ * Add two integers, checking for overflow.
+ *
+ * @param x an addend
+ * @param y an addend
+ * @return the sum <code>x+y</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * int
+ * @since 1.1
+ */
+ public static int addAndCheck(int x, int y) {
+ long s = (long)x + (long)y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw MathRuntimeException.createArithmeticException(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</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * long
+ * @since 1.2
+ */
+ public static long addAndCheck(long a, long b) {
+ return addAndCheck(a, b, LocalizedFormats.OVERFLOW_IN_ADDITION);
+ }
+
+ /**
+ * Add two long integers, checking for overflow.
+ *
+ * @param a an addend
+ * @param b an addend
+ * @param pattern the pattern to use for any thrown exception.
+ * @return the sum <code>a+b</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * long
+ * @since 1.2
+ */
+ private static long addAndCheck(long a, long b, Localizable pattern) {
+ long ret;
+ if (a > b) {
+ // use symmetry to reduce boundary cases
+ ret = addAndCheck(b, a, pattern);
+ } else {
+ // assert a <= b
+
+ if (a < 0) {
+ if (b < 0) {
+ // check for negative overflow
+ if (Long.MIN_VALUE - b <= a) {
+ ret = a + b;
+ } else {
+ throw MathRuntimeException.createArithmeticException(pattern, a, b);
+ }
+ } else {
+ // opposite sign addition is always safe
+ ret = a + b;
+ }
+ } else {
+ // assert a >= 0
+ // assert b >= 0
+
+ // check for positive overflow
+ if (a <= Long.MAX_VALUE - b) {
+ ret = a + b;
+ } else {
+ throw MathRuntimeException.createArithmeticException(pattern, a, b);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns an exact representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
+ * Coefficient</a>, "<code>n choose k</code>", the number of
+ * <code>k</code>-element subsets that can be selected from an
+ * <code>n</code>-element set.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>0 <= k <= n </code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * <li> The result is small enough to fit into a <code>long</code>. The
+ * largest value of <code>n</code> for which all coefficients are
+ * <code> < Long.MAX_VALUE</code> is 66. If the computed value exceeds
+ * <code>Long.MAX_VALUE</code> an <code>ArithMeticException</code> is
+ * thrown.</li>
+ * </ul></p>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return <code>n choose k</code>
+ * @throws IllegalArgumentException if preconditions are not met.
+ * @throws ArithmeticException if the result is too large to be represented
+ * by a long integer.
+ */
+ public static long binomialCoefficient(final int n, final int k) {
+ 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 = 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 = gcd(i, j);
+ result = mulAndCheck(result / (j / d), i / d);
+ i++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a <code>double</code> representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
+ * Coefficient</a>, "<code>n choose k</code>", the number of
+ * <code>k</code>-element subsets that can be selected from an
+ * <code>n</code>-element set.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>0 <= k <= n </code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * <li> The result is small enough to fit into a <code>double</code>. The
+ * largest value of <code>n</code> for which all coefficients are <
+ * Double.MAX_VALUE is 1029. If the computed value exceeds Double.MAX_VALUE,
+ * Double.POSITIVE_INFINITY is returned</li>
+ * </ul></p>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return <code>n choose k</code>
+ * @throws IllegalArgumentException if preconditions are not met.
+ */
+ public static double binomialCoefficientDouble(final int n, final int k) {
+ 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</code> of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
+ * Coefficient</a>, "<code>n choose k</code>", the number of
+ * <code>k</code>-element subsets that can be selected from an
+ * <code>n</code>-element set.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>0 <= k <= n </code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * </ul></p>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return <code>n choose k</code>
+ * @throws IllegalArgumentException if preconditions are not met.
+ */
+ public static double binomialCoefficientLog(final int n, final int k) {
+ 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;
+ }
+
+ /**
+ * Check binomial preconditions.
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @exception IllegalArgumentException if preconditions are not met.
+ */
+ private static void checkBinomial(final int n, final int k)
+ throws IllegalArgumentException {
+ if (n < k) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.BINOMIAL_INVALID_PARAMETERS_ORDER,
+ n, k);
+ }
+ if (n < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.BINOMIAL_NEGATIVE_PARAMETER,
+ n);
+ }
+ }
+
+ /**
+ * 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>
+ * <li>&lt; 0 if !{@link #equals(double, double, double) equals(x, y, eps)} &amp;&amp; x &lt; y</li>
+ * <li>> 0 if !{@link #equals(double, double, double) equals(x, y, eps)} &amp;&amp; x > y</li></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;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/HyperbolicCosine.html">
+ * hyperbolic cosine</a> of x.
+ *
+ * @param x double value for which to find the hyperbolic cosine
+ * @return hyperbolic cosine of x
+ */
+ public static double cosh(double x) {
+ return (FastMath.exp(x) + FastMath.exp(-x)) / 2.0;
+ }
+
+ /**
+ * Returns true iff they are strictly equal.
+ *
+ * @param x first value
+ * @param y second value
+ * @return {@code true} if the values are equal.
+ * @deprecated as of 2.2 his method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases wher the old semantics is
+ * useful (see e.g. {@link #equalsIncludingNaN(float,float)
+ * equalsIncludingNaN}.
+ */
+ @Deprecated
+ public static boolean equals(float x, float y) {
+ return (Float.isNaN(x) && Float.isNaN(y)) || x == y;
+ }
+
+ /**
+ * Returns true if both arguments are NaN or neither is NaN and 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 or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(float x, float y) {
+ return (Float.isNaN(x) && Float.isNaN(y)) || equals(x, y, 1);
+ }
+
+ /**
+ * Returns true if both arguments are equal or 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.
+ * @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 both arguments are NaN or are equal or 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 both 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://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm">
+ * Bruce Dawson</a>
+ *
+ * @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(float x, float y, int maxUlps) {
+ // Check that "maxUlps" is non-negative and small enough so that
+ // NaN won't compare as equal to anything (except another NaN).
+ assert maxUlps > 0 && maxUlps < NAN_GAP;
+
+ int xInt = Float.floatToIntBits(x);
+ int yInt = Float.floatToIntBits(y);
+
+ // Make lexicographically ordered as a two's-complement integer.
+ if (xInt < 0) {
+ xInt = SGN_MASK_FLOAT - xInt;
+ }
+ if (yInt < 0) {
+ yInt = SGN_MASK_FLOAT - yInt;
+ }
+
+ final boolean isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+
+ return isEqual && !Float.isNaN(x) && !Float.isNaN(y);
+ }
+
+ /**
+ * Returns true if both arguments are 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 (Float.isNaN(x) && Float.isNaN(y)) || equals(x, y, maxUlps);
+ }
+
+ /**
+ * Returns true iff both arguments are null or have same dimensions and all
+ * their elements are equal as defined by
+ * {@link #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.
+ * @deprecated as of 2.2 this method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases where the old semantics is
+ * useful (see e.g. {@link #equalsIncludingNaN(float[],float[])
+ * equalsIncludingNaN}.
+ */
+ @Deprecated
+ 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 (!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 #equalsIncludingNaN(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
+ * @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 (!equalsIncludingNaN(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true iff both arguments are NaN or neither is NaN and they are
+ * equal
+ *
+ * <p>This method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases where the old semantics
+ * (w.r.t. NaN) is useful (see e.g.
+ * {@link #equalsIncludingNaN(double,double, double) equalsIncludingNaN}.
+ * </p>
+ *
+ * @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 (Double.isNaN(x) && Double.isNaN(y)) || x == y;
+ }
+
+ /**
+ * Returns true if both arguments are NaN or neither is NaN and 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 or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(double x, double y) {
+ return (Double.isNaN(x) && Double.isNaN(y)) || equals(x, y, 1);
+ }
+
+ /**
+ * Returns true if both arguments are equal or within the range of allowed
+ * error (inclusive).
+ * <p>
+ * Two NaNs are considered equals, as are two infinities with same sign.
+ * </p>
+ * <p>This method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases where the old semantics
+ * (w.r.t. NaN) is useful (see e.g.
+ * {@link #equalsIncludingNaN(double,double, double) equalsIncludingNaN}.
+ * </p>
+ * @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.
+ */
+ public static boolean equals(double x, double y, double eps) {
+ return equals(x, y) || FastMath.abs(y - x) <= eps;
+ }
+
+ /**
+ * Returns true if both arguments are NaN or are equal or 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 both 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://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm">
+ * Bruce Dawson</a>
+ *
+ * <p>This method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases where the old semantics
+ * (w.r.t. NaN) is useful (see e.g.
+ * {@link #equalsIncludingNaN(double,double, int) equalsIncludingNaN}.
+ * </p>
+ *
+ * @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(double x, double y, int maxUlps) {
+ // Check that "maxUlps" is non-negative and small enough so that
+ // NaN won't compare as equal to anything (except another NaN).
+ assert maxUlps > 0 && maxUlps < NAN_GAP;
+
+ long xInt = Double.doubleToLongBits(x);
+ long yInt = Double.doubleToLongBits(y);
+
+ // Make lexicographically ordered as a two's-complement integer.
+ if (xInt < 0) {
+ xInt = SGN_MASK - xInt;
+ }
+ if (yInt < 0) {
+ yInt = SGN_MASK - yInt;
+ }
+
+ return FastMath.abs(xInt - yInt) <= maxUlps;
+ }
+
+ /**
+ * 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 (Double.isNaN(x) && Double.isNaN(y)) || equals(x, y, maxUlps);
+ }
+
+ /**
+ * Returns true iff both arguments are null or have same dimensions and all
+ * their elements are equal as defined by
+ * {@link #equals(double,double)}.
+ *
+ * <p>This method considers that {@code NaN == NaN}. In release
+ * 3.0, the semantics will change in order to comply with IEEE754 where it
+ * is specified that {@code NaN != NaN}.
+ * New methods have been added for those cases wher the old semantics is
+ * useful (see e.g. {@link #equalsIncludingNaN(double[],double[])
+ * equalsIncludingNaN}.
+ * </p>
+ * @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(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 (!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 #equalsIncludingNaN(double,double)}.
+ *
+ * @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(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 (!equalsIncludingNaN(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns n!. Shorthand for <code>n</code> <a
+ * href="http://mathworld.wolfram.com/Factorial.html"> Factorial</a>, the
+ * product of the numbers <code>1,...,n</code>.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>n >= 0</code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * <li> The result is small enough to fit into a <code>long</code>. The
+ * largest value of <code>n</code> for which <code>n!</code> <
+ * Long.MAX_VALUE</code> is 20. If the computed value exceeds <code>Long.MAX_VALUE</code>
+ * an <code>ArithMeticException </code> is thrown.</li>
+ * </ul>
+ * </p>
+ *
+ * @param n argument
+ * @return <code>n!</code>
+ * @throws ArithmeticException if the result is too large to be represented
+ * by a long integer.
+ * @throws IllegalArgumentException if n < 0
+ */
+ public static long factorial(final int n) {
+ if (n < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER,
+ n);
+ }
+ if (n > 20) {
+ throw new ArithmeticException(
+ "factorial value is too large to fit in a long");
+ }
+ return FACTORIALS[n];
+ }
+
+ /**
+ * Returns n!. Shorthand for <code>n</code> <a
+ * href="http://mathworld.wolfram.com/Factorial.html"> Factorial</a>, the
+ * product of the numbers <code>1,...,n</code> as a <code>double</code>.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>n >= 0</code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * <li> The result is small enough to fit into a <code>double</code>. The
+ * largest value of <code>n</code> for which <code>n!</code> <
+ * Double.MAX_VALUE</code> is 170. If the computed value exceeds
+ * Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned</li>
+ * </ul>
+ * </p>
+ *
+ * @param n argument
+ * @return <code>n!</code>
+ * @throws IllegalArgumentException if n < 0
+ */
+ public static double factorialDouble(final int n) {
+ if (n < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER,
+ n);
+ }
+ if (n < 21) {
+ return factorial(n);
+ }
+ return FastMath.floor(FastMath.exp(factorialLog(n)) + 0.5);
+ }
+
+ /**
+ * Returns the natural logarithm of n!.
+ * <p>
+ * <Strong>Preconditions</strong>:
+ * <ul>
+ * <li> <code>n >= 0</code> (otherwise
+ * <code>IllegalArgumentException</code> is thrown)</li>
+ * </ul></p>
+ *
+ * @param n argument
+ * @return <code>n!</code>
+ * @throws IllegalArgumentException if preconditions are not met.
+ */
+ public static double factorialLog(final int n) {
+ if (n < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER,
+ n);
+ }
+ if (n < 21) {
+ return FastMath.log(factorial(n));
+ }
+ double logSum = 0;
+ for (int i = 2; i <= n; i++) {
+ logSum += FastMath.log(i);
+ }
+ return logSum;
+ }
+
+ /**
+ * <p>
+ * 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).
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations
+ * <code>gcd(Integer.MIN_VALUE, Integer.MIN_VALUE)</code>,
+ * <code>gcd(Integer.MIN_VALUE, 0)</code> and
+ * <code>gcd(0, Integer.MIN_VALUE)</code> throw an
+ * <code>ArithmeticException</code>, because the result would be 2^31, which
+ * is too large for an int value.</li>
+ * <li>The result of <code>gcd(x, x)</code>, <code>gcd(0, x)</code> and
+ * <code>gcd(x, 0)</code> is the absolute value of <code>x</code>, except
+ * for the special cases above.
+ * <li>The invocation <code>gcd(0, 0)</code> is the only one which returns
+ * <code>0</code>.</li>
+ * </ul>
+ *
+ * @param p any number
+ * @param q any number
+ * @return the greatest common divisor, never negative
+ * @throws ArithmeticException if the result cannot be represented as a
+ * nonnegative int value
+ * @since 1.1
+ */
+ public static int gcd(final int p, final int q) {
+ int u = p;
+ int v = q;
+ if ((u == 0) || (v == 0)) {
+ if ((u == Integer.MIN_VALUE) || (v == Integer.MIN_VALUE)) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.GCD_OVERFLOW_32_BITS,
+ p, q);
+ }
+ return FastMath.abs(u) + FastMath.abs(v);
+ }
+ // keep u and v negative, as negative integers range down to
+ // -2^31, while positive numbers can only be as large as 2^31-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 < 31) { // while u and v are
+ // both even...
+ u /= 2;
+ v /= 2;
+ k++; // cast out twos.
+ }
+ if (k == 31) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.GCD_OVERFLOW_32_BITS,
+ p, q);
+ }
+ // B2. Initialize: u and v have been divided by 2^k and at least
+ // one is odd.
+ int 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 * (1 << k); // gcd is u*2^k
+ }
+
+ /**
+ * <p>
+ * 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).
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations
+ * <code>gcd(Long.MIN_VALUE, Long.MIN_VALUE)</code>,
+ * <code>gcd(Long.MIN_VALUE, 0L)</code> and
+ * <code>gcd(0L, Long.MIN_VALUE)</code> throw an
+ * <code>ArithmeticException</code>, because the result would be 2^63, which
+ * is too large for a long value.</li>
+ * <li>The result of <code>gcd(x, x)</code>, <code>gcd(0L, x)</code> and
+ * <code>gcd(x, 0L)</code> is the absolute value of <code>x</code>, except
+ * for the special cases above.
+ * <li>The invocation <code>gcd(0L, 0L)</code> is the only one which returns
+ * <code>0L</code>.</li>
+ * </ul>
+ *
+ * @param p any number
+ * @param q any number
+ * @return the greatest common divisor, never negative
+ * @throws ArithmeticException if the result cannot be represented as a nonnegative long
+ * value
+ * @since 2.1
+ */
+ public static long gcd(final long p, final long q) {
+ long u = p;
+ long v = q;
+ if ((u == 0) || (v == 0)) {
+ if ((u == Long.MIN_VALUE) || (v == Long.MIN_VALUE)){
+ throw MathRuntimeException.createArithmeticException(
+ 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 MathRuntimeException.createArithmeticException(
+ 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 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 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);
+ }
+
+ /**
+ * For a byte value x, this method returns (byte)(+1) if x >= 0 and
+ * (byte)(-1) if x < 0.
+ *
+ * @param x the value, a byte
+ * @return (byte)(+1) or (byte)(-1), depending on the sign of x
+ */
+ public static byte indicator(final byte x) {
+ return (x >= ZB) ? PB : NB;
+ }
+
+ /**
+ * For a double precision value x, this method returns +1.0 if x >= 0 and
+ * -1.0 if x < 0. Returns <code>NaN</code> if <code>x</code> is
+ * <code>NaN</code>.
+ *
+ * @param x the value, a double
+ * @return +1.0 or -1.0, depending on the sign of x
+ */
+ public static double indicator(final double x) {
+ if (Double.isNaN(x)) {
+ return Double.NaN;
+ }
+ return (x >= 0.0) ? 1.0 : -1.0;
+ }
+
+ /**
+ * For a float value x, this method returns +1.0F if x >= 0 and -1.0F if x <
+ * 0. Returns <code>NaN</code> if <code>x</code> is <code>NaN</code>.
+ *
+ * @param x the value, a float
+ * @return +1.0F or -1.0F, depending on the sign of x
+ */
+ public static float indicator(final float x) {
+ if (Float.isNaN(x)) {
+ return Float.NaN;
+ }
+ return (x >= 0.0F) ? 1.0F : -1.0F;
+ }
+
+ /**
+ * For an int value x, this method returns +1 if x >= 0 and -1 if x < 0.
+ *
+ * @param x the value, an int
+ * @return +1 or -1, depending on the sign of x
+ */
+ public static int indicator(final int x) {
+ return (x >= 0) ? 1 : -1;
+ }
+
+ /**
+ * For a long value x, this method returns +1L if x >= 0 and -1L if x < 0.
+ *
+ * @param x the value, a long
+ * @return +1L or -1L, depending on the sign of x
+ */
+ public static long indicator(final long x) {
+ return (x >= 0L) ? 1L : -1L;
+ }
+
+ /**
+ * For a short value x, this method returns (short)(+1) if x >= 0 and
+ * (short)(-1) if x < 0.
+ *
+ * @param x the value, a short
+ * @return (short)(+1) or (short)(-1), depending on the sign of x
+ */
+ public static short indicator(final short x) {
+ return (x >= ZS) ? PS : NS;
+ }
+
+ /**
+ * <p>
+ * Returns the least common multiple of the absolute value of two numbers,
+ * using the formula <code>lcm(a,b) = (a / gcd(a,b)) * b</code>.
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations <code>lcm(Integer.MIN_VALUE, n)</code> and
+ * <code>lcm(n, Integer.MIN_VALUE)</code>, where <code>abs(n)</code> is a
+ * power of 2, throw an <code>ArithmeticException</code>, because the result
+ * would be 2^31, which is too large for an int value.</li>
+ * <li>The result of <code>lcm(0, x)</code> and <code>lcm(x, 0)</code> is
+ * <code>0</code> for any <code>x</code>.
+ * </ul>
+ *
+ * @param a any number
+ * @param b any number
+ * @return the least common multiple, never negative
+ * @throws ArithmeticException
+ * if the result cannot be represented as a nonnegative int
+ * value
+ * @since 1.1
+ */
+ public static int lcm(int a, int b) {
+ if (a==0 || b==0){
+ return 0;
+ }
+ int lcm = FastMath.abs(mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Integer.MIN_VALUE) {
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.LCM_OVERFLOW_32_BITS,
+ a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * <p>
+ * Returns the least common multiple of the absolute value of two numbers,
+ * using the formula <code>lcm(a,b) = (a / gcd(a,b)) * b</code>.
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations <code>lcm(Long.MIN_VALUE, n)</code> and
+ * <code>lcm(n, Long.MIN_VALUE)</code>, where <code>abs(n)</code> is a
+ * power of 2, throw an <code>ArithmeticException</code>, because the result
+ * would be 2^63, which is too large for an int value.</li>
+ * <li>The result of <code>lcm(0L, x)</code> and <code>lcm(x, 0L)</code> is
+ * <code>0L</code> for any <code>x</code>.
+ * </ul>
+ *
+ * @param a any number
+ * @param b any number
+ * @return the least common multiple, never negative
+ * @throws ArithmeticException if the result cannot be represented as a nonnegative long
+ * value
+ * @since 2.1
+ */
+ public static long lcm(long a, long b) {
+ if (a==0 || b==0){
+ return 0;
+ }
+ long lcm = FastMath.abs(mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Long.MIN_VALUE){
+ throw MathRuntimeException.createArithmeticException(
+ LocalizedFormats.LCM_OVERFLOW_64_BITS,
+ a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * <p>Returns the
+ * <a href="http://mathworld.wolfram.com/Logarithm.html">logarithm</a>
+ * for base <code>b</code> of <code>x</code>.
+ * </p>
+ * <p>Returns <code>NaN<code> if either argument is negative. If
+ * <code>base</code> is 0 and <code>x</code> is positive, 0 is returned.
+ * If <code>base</code> is positive and <code>x</code> is 0,
+ * <code>Double.NEGATIVE_INFINITY</code> is returned. If both arguments
+ * are 0, the result is <code>NaN</code>.</p>
+ *
+ * @param base the base of the logarithm, must be greater than 0
+ * @param x argument, must be greater than 0
+ * @return the value of the logarithm - the number y such that base^y = x.
+ * @since 1.2
+ */
+ public static double log(double base, double x) {
+ return FastMath.log(x)/FastMath.log(base);
+ }
+
+ /**
+ * Multiply two integers, checking for overflow.
+ *
+ * @param x a factor
+ * @param y a factor
+ * @return the product <code>x*y</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * int
+ * @since 1.1
+ */
+ public static int mulAndCheck(int x, int y) {
+ long m = ((long)x) * ((long)y);
+ if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
+ throw new ArithmeticException("overflow: mul");
+ }
+ return (int)m;
+ }
+
+ /**
+ * Multiply two long integers, checking for overflow.
+ *
+ * @param a first value
+ * @param b second value
+ * @return the product <code>a * b</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * long
+ * @since 1.2
+ */
+ public static long mulAndCheck(long a, long b) {
+ long ret;
+ String msg = "overflow: multiply";
+ 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 ArithmeticException(msg);
+ }
+ } 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 ArithmeticException(msg);
+
+ }
+ } 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 ArithmeticException(msg);
+ }
+ } else {
+ // assert a == 0
+ ret = 0;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Get the next machine representable number after a number, moving
+ * in the direction of another number.
+ * <p>
+ * If <code>direction</code> is greater than or equal to<code>d</code>,
+ * the smallest machine representable number strictly greater than
+ * <code>d</code> is returned; otherwise the largest representable number
+ * strictly less than <code>d</code> is returned.</p>
+ * <p>
+ * If <code>d</code> is NaN or Infinite, it is returned unchanged.</p>
+ *
+ * @param d base number
+ * @param direction (the only important thing is whether
+ * direction is greater or smaller than d)
+ * @return the next machine representable number in the specified direction
+ * @since 1.2
+ * @deprecated as of 2.2, replaced by {@link FastMath#nextAfter(double, double)}
+ * which handles Infinities differently, and returns direction if d and direction compare equal.
+ */
+ @Deprecated
+ public static double nextAfter(double d, double direction) {
+
+ // handling of some important special cases
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
+ return d;
+ } 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
+
+ // split the double in raw components
+ long bits = Double.doubleToLongBits(d);
+ long sign = bits & 0x8000000000000000L;
+ long exponent = bits & 0x7ff0000000000000L;
+ long mantissa = bits & 0x000fffffffffffffL;
+
+ if (d * (direction - d) >= 0) {
+ // we should increase the mantissa
+ if (mantissa == 0x000fffffffffffffL) {
+ return Double.longBitsToDouble(sign |
+ (exponent + 0x0010000000000000L));
+ } else {
+ return Double.longBitsToDouble(sign |
+ exponent | (mantissa + 1));
+ }
+ } else {
+ // we should decrease the mantissa
+ if (mantissa == 0L) {
+ return Double.longBitsToDouble(sign |
+ (exponent - 0x0010000000000000L) |
+ 0x000fffffffffffffL);
+ } else {
+ return Double.longBitsToDouble(sign |
+ exponent | (mantissa - 1));
+ }
+ }
+ }
+
+ /**
+ * Scale a number by 2<sup>scaleFactor</sup>.
+ * <p>If <code>d</code> is 0 or NaN or Infinite, it is returned unchanged.</p>
+ *
+ * @param d base number
+ * @param scaleFactor power of two by which d should be multiplied
+ * @return d &times; 2<sup>scaleFactor</sup>
+ * @since 2.0
+ * @deprecated as of 2.2, replaced by {@link FastMath#scalb(double, int)}
+ */
+ @Deprecated
+ public static double scalb(final double d, final int scaleFactor) {
+ return FastMath.scalb(d, scaleFactor);
+ }
+
+ /**
+ * Normalize an angle in a 2&pi wide interval around a center value.
+ * <p>This method has three main uses:</p>
+ * <ul>
+ * <li>normalize an angle between 0 and 2&pi;:<br/>
+ * <code>a = MathUtils.normalizeAngle(a, FastMath.PI);</code></li>
+ * <li>normalize an angle between -&pi; and +&pi;<br/>
+ * <code>a = MathUtils.normalizeAngle(a, 0.0);</code></li>
+ * <li>compute the angle between two defining angular positions:<br>
+ * <code>angle = MathUtils.normalizeAngle(end, start) - start;</code></li>
+ * </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.</p>
+ * @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);
+ }
+
+ /**
+ * <p>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>
+ *
+ * <p>Throws IllegalArgumentException if <code>normalizedSum</code> is infinite
+ * or NaN and ArithmeticException if the input array contains any infinite elements
+ * or sums to 0</p>
+ *
+ * <p>Ignores (i.e., copies unchanged to the output array) NaNs in the input array.</p>
+ *
+ * @param values input array to be normalized
+ * @param normalizedSum target sum for the normalized array
+ * @return normalized array
+ * @throws ArithmeticException if the input array contains infinite elements or sums to zero
+ * @throws IllegalArgumentException if the target sum is infinite or NaN
+ * @since 2.1
+ */
+ public static double[] normalizeArray(double[] values, double normalizedSum)
+ throws ArithmeticException, IllegalArgumentException {
+ if (Double.isInfinite(normalizedSum)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.NORMALIZE_INFINITE);
+ }
+ if (Double.isNaN(normalizedSum)) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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 MathRuntimeException.createArithmeticException(
+ LocalizedFormats.INFINITE_ARRAY_ELEMENT, values[i], i);
+ }
+ if (!Double.isNaN(values[i])) {
+ sum += values[i];
+ }
+ }
+ if (sum == 0) {
+ throw MathRuntimeException.createArithmeticException(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;
+ }
+
+ /**
+ * Round the given value to the specified number of decimal places. The
+ * value is rounded using the {@link BigDecimal#ROUND_HALF_UP} method.
+ *
+ * @param x the value to round.
+ * @param scale the number of digits to the right of the decimal point.
+ * @return the rounded value.
+ * @since 1.1
+ */
+ public static double round(double x, int scale) {
+ return round(x, scale, BigDecimal.ROUND_HALF_UP);
+ }
+
+ /**
+ * Round 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 the value to round.
+ * @param scale the number of digits to the right of the decimal point.
+ * @param roundingMethod the rounding method as defined in
+ * {@link BigDecimal}.
+ * @return the rounded value.
+ * @since 1.1
+ */
+ public static double round(double x, int scale, int roundingMethod) {
+ try {
+ return (new BigDecimal
+ (Double.toString(x))
+ .setScale(scale, roundingMethod))
+ .doubleValue();
+ } catch (NumberFormatException ex) {
+ if (Double.isInfinite(x)) {
+ return x;
+ } else {
+ return Double.NaN;
+ }
+ }
+ }
+
+ /**
+ * Round the given value to the specified number of decimal places. The
+ * value is rounding using the {@link BigDecimal#ROUND_HALF_UP} method.
+ *
+ * @param x the value to round.
+ * @param scale the number of digits to the right of the decimal point.
+ * @return the rounded value.
+ * @since 1.1
+ */
+ public static float round(float x, int scale) {
+ return round(x, scale, BigDecimal.ROUND_HALF_UP);
+ }
+
+ /**
+ * Round 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 the value to round.
+ * @param scale the number of digits to the right of the decimal point.
+ * @param roundingMethod the rounding method as defined in
+ * {@link BigDecimal}.
+ * @return the rounded value.
+ * @since 1.1
+ */
+ public static float round(float x, int scale, int roundingMethod) {
+ float sign = indicator(x);
+ float factor = (float)FastMath.pow(10.0f, scale) * sign;
+ return (float)roundUnscaled(x * factor, sign, roundingMethod) / factor;
+ }
+
+ /**
+ * Round 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 the value to round.
+ * @param sign the sign of the original, scaled value.
+ * @param roundingMethod the rounding method as defined in
+ * {@link BigDecimal}.
+ * @return the rounded value.
+ * @since 1.1
+ */
+ private static double roundUnscaled(double unscaled, double sign,
+ int roundingMethod) {
+ switch (roundingMethod) {
+ case BigDecimal.ROUND_CEILING :
+ if (sign == -1) {
+ unscaled = FastMath.floor(nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ } else {
+ unscaled = FastMath.ceil(nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ }
+ break;
+ case BigDecimal.ROUND_DOWN :
+ unscaled = FastMath.floor(nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ break;
+ case BigDecimal.ROUND_FLOOR :
+ if (sign == -1) {
+ unscaled = FastMath.ceil(nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ } else {
+ unscaled = FastMath.floor(nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ }
+ break;
+ case BigDecimal.ROUND_HALF_DOWN : {
+ unscaled = 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(Math
+ .floor(unscaled) / 2.0)) { // even
+ unscaled = FastMath.floor(unscaled);
+ } else { // odd
+ unscaled = FastMath.ceil(unscaled);
+ }
+ }
+ break;
+ }
+ case BigDecimal.ROUND_HALF_UP : {
+ unscaled = 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 ArithmeticException("Inexact result from rounding");
+ }
+ break;
+ case BigDecimal.ROUND_UP :
+ unscaled = FastMath.ceil(nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ break;
+ default :
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for byte value <code>x</code>.
+ * <p>
+ * For a byte value x, this method returns (byte)(+1) if x > 0, (byte)(0) if
+ * x = 0, and (byte)(-1) if x < 0.</p>
+ *
+ * @param x the value, a byte
+ * @return (byte)(+1), (byte)(0), or (byte)(-1), depending on the sign of x
+ */
+ public static byte sign(final byte x) {
+ return (x == ZB) ? ZB : (x > ZB) ? PB : NB;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for double precision <code>x</code>.
+ * <p>
+ * For a double value <code>x</code>, this method returns
+ * <code>+1.0</code> if <code>x > 0</code>, <code>0.0</code> if
+ * <code>x = 0.0</code>, and <code>-1.0</code> if <code>x < 0</code>.
+ * Returns <code>NaN</code> if <code>x</code> is <code>NaN</code>.</p>
+ *
+ * @param x the value, a double
+ * @return +1.0, 0.0, or -1.0, depending on the sign of x
+ */
+ public static double sign(final double x) {
+ if (Double.isNaN(x)) {
+ return Double.NaN;
+ }
+ return (x == 0.0) ? 0.0 : (x > 0.0) ? 1.0 : -1.0;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for float value <code>x</code>.
+ * <p>
+ * For a float value x, this method returns +1.0F if x > 0, 0.0F if x =
+ * 0.0F, and -1.0F if x < 0. Returns <code>NaN</code> if <code>x</code>
+ * is <code>NaN</code>.</p>
+ *
+ * @param x the value, a float
+ * @return +1.0F, 0.0F, or -1.0F, depending on the sign of x
+ */
+ public static float sign(final float x) {
+ if (Float.isNaN(x)) {
+ return Float.NaN;
+ }
+ return (x == 0.0F) ? 0.0F : (x > 0.0F) ? 1.0F : -1.0F;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for int value <code>x</code>.
+ * <p>
+ * For an int value x, this method returns +1 if x > 0, 0 if x = 0, and -1
+ * if x < 0.</p>
+ *
+ * @param x the value, an int
+ * @return +1, 0, or -1, depending on the sign of x
+ */
+ public static int sign(final int x) {
+ return (x == 0) ? 0 : (x > 0) ? 1 : -1;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for long value <code>x</code>.
+ * <p>
+ * For a long value x, this method returns +1L if x > 0, 0L if x = 0, and
+ * -1L if x < 0.</p>
+ *
+ * @param x the value, a long
+ * @return +1L, 0L, or -1L, depending on the sign of x
+ */
+ public static long sign(final long x) {
+ return (x == 0L) ? 0L : (x > 0L) ? 1L : -1L;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/Sign.html"> sign</a>
+ * for short value <code>x</code>.
+ * <p>
+ * For a short value x, this method returns (short)(+1) if x > 0, (short)(0)
+ * if x = 0, and (short)(-1) if x < 0.</p>
+ *
+ * @param x the value, a short
+ * @return (short)(+1), (short)(0), or (short)(-1), depending on the sign of
+ * x
+ */
+ public static short sign(final short x) {
+ return (x == ZS) ? ZS : (x > ZS) ? PS : NS;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/HyperbolicSine.html">
+ * hyperbolic sine</a> of x.
+ *
+ * @param x double value for which to find the hyperbolic sine
+ * @return hyperbolic sine of x
+ */
+ public static double sinh(double x) {
+ return (FastMath.exp(x) - FastMath.exp(-x)) / 2.0;
+ }
+
+ /**
+ * Subtract two integers, checking for overflow.
+ *
+ * @param x the minuend
+ * @param y the subtrahend
+ * @return the difference <code>x-y</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * int
+ * @since 1.1
+ */
+ public static int subAndCheck(int x, int y) {
+ long s = (long)x - (long)y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw MathRuntimeException.createArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, x, y);
+ }
+ return (int)s;
+ }
+
+ /**
+ * Subtract two long integers, checking for overflow.
+ *
+ * @param a first value
+ * @param b second value
+ * @return the difference <code>a-b</code>
+ * @throws ArithmeticException if the result can not be represented as an
+ * long
+ * @since 1.2
+ */
+ public static long subAndCheck(long a, long b) {
+ long ret;
+ String msg = "overflow: subtract";
+ if (b == Long.MIN_VALUE) {
+ if (a < 0) {
+ ret = a - b;
+ } else {
+ throw new ArithmeticException(msg);
+ }
+ } 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 null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static int pow(final int k, int e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ int result = 1;
+ int k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e = e >> 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise an int to a long power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static int pow(final int k, long e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ int result = 1;
+ int k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e = e >> 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise a long to an int power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static long pow(final long k, int e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ long result = 1l;
+ long k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e = e >> 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise a long to a long power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static long pow(final long k, long e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ long result = 1l;
+ long k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e = e >> 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise a BigInteger to an int power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static BigInteger pow(final BigInteger k, int e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ return k.pow(e);
+
+ }
+
+ /**
+ * Raise a BigInteger to a long power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static BigInteger pow(final BigInteger k, long e)
+ throws IllegalArgumentException {
+
+ if (e < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, e);
+ }
+
+ BigInteger result = BigInteger.ONE;
+ BigInteger k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result = result.multiply(k2p);
+ }
+ k2p = k2p.multiply(k2p);
+ e = e >> 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise a BigInteger to a BigInteger power.
+ * @param k number to raise
+ * @param e exponent (must be positive or null)
+ * @return k<sup>e</sup>
+ * @exception IllegalArgumentException if e is negative
+ */
+ public static BigInteger pow(final BigInteger k, BigInteger e)
+ throws IllegalArgumentException {
+
+ if (e.compareTo(BigInteger.ZERO) < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.POWER_NEGATIVE_PARAMETERS,
+ k, 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;
+
+ }
+
+ /**
+ * 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
+ */
+ public static double distance1(double[] p1, double[] 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
+ */
+ public static int distance1(int[] p1, int[] 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
+ */
+ public static double distance(double[] p1, double[] 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>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
+ */
+ public static double distance(int[] p1, int[] 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
+ */
+ public static double distanceInf(double[] p1, double[] 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
+ */
+ public static int distanceInf(int[] p1, int[] 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 static enum OrderDirection {
+ /** Constant for increasing direction. */
+ INCREASING,
+ /** Constant for decreasing direction. */
+ DECREASING
+ }
+
+ /**
+ * Checks that the given array is sorted.
+ *
+ * @param val Values.
+ * @param dir Ordering direction.
+ * @param strict Whether the order should be strict.
+ * @throws NonMonotonousSequenceException if the array is not sorted.
+ * @since 2.2
+ */
+ public static void checkOrder(double[] val, OrderDirection dir, boolean strict) {
+ double previous = val[0];
+ boolean ok = true;
+
+ int max = val.length;
+ for (int i = 1; i < max; i++) {
+ switch (dir) {
+ case INCREASING:
+ if (strict) {
+ if (val[i] <= previous) {
+ ok = false;
+ }
+ } else {
+ if (val[i] < previous) {
+ ok = false;
+ }
+ }
+ break;
+ case DECREASING:
+ if (strict) {
+ if (val[i] >= previous) {
+ ok = false;
+ }
+ } else {
+ if (val[i] > previous) {
+ ok = false;
+ }
+ }
+ break;
+ default:
+ // Should never happen.
+ throw new IllegalArgumentException();
+ }
+
+ if (!ok) {
+ throw new NonMonotonousSequenceException(val[i], previous, i, dir, strict);
+ }
+ previous = val[i];
+ }
+ }
+
+ /**
+ * Checks that the given array is sorted in strictly increasing order.
+ *
+ * @param val Values.
+ * @throws NonMonotonousSequenceException if the array is not sorted.
+ * @since 2.2
+ */
+ public static void checkOrder(double[] val) {
+ checkOrder(val, OrderDirection.INCREASING, true);
+ }
+
+ /**
+ * Checks that the given array is sorted.
+ *
+ * @param val Values
+ * @param dir Order direction (-1 for decreasing, 1 for increasing)
+ * @param strict Whether the order should be strict
+ * @throws NonMonotonousSequenceException if the array is not sorted.
+ * @deprecated as of 2.2 (please use the new {@link #checkOrder(double[],OrderDirection,boolean)
+ * checkOrder} method). To be removed in 3.0.
+ */
+ @Deprecated
+ public static void checkOrder(double[] val, int dir, boolean strict) {
+ if (dir > 0) {
+ checkOrder(val, OrderDirection.INCREASING, strict);
+ } else {
+ checkOrder(val, OrderDirection.DECREASING, strict);
+ }
+ }
+
+ /**
+ * Returns the Cartesian norm (2-norm), handling both overflow and underflow.
+ * Translation of the minpack enorm subroutine.
+ *
+ * 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>
+ *
+ * @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.0;
+ double s2=0.0;
+ double s3=0.0;
+ double x1max = 0.0;
+ double x3max = 0.0;
+ double floatn = (double)v.length;
+ double agiant = rgiant/floatn;
+ for (int i=0;i<v.length;i++) {
+ double xabs = Math.abs(v[i]);
+ if (xabs<rdwarf || xabs>agiant) {
+ if (xabs>rdwarf) {
+ if (xabs>x1max) {
+ double r=x1max/xabs;
+ s1=1.0+s1*r*r;
+ x1max=xabs;
+ } else {
+ double r=xabs/x1max;
+ s1+=r*r;
+ }
+ } else {
+ if (xabs>x3max) {
+ double r=x3max/xabs;
+ s3=1.0+s3*r*r;
+ x3max=xabs;
+ } else {
+ if (xabs!=0.0) {
+ double r=xabs/x3max;
+ s3+=r*r;
+ }
+ }
+ }
+ } else {
+ s2+=xabs*xabs;
+ }
+ }
+ double norm;
+ if (s1!=0.0) {
+ norm = x1max*Math.sqrt(s1+(s2/x1max)/x1max);
+ } else {
+ if (s2==0.0) {
+ norm = x3max*Math.sqrt(s3);
+ } else {
+ if (s2>=x3max) {
+ norm = Math.sqrt(s2*(1.0+(x3max/s2)*(x3max*s3)));
+ } else {
+ norm = Math.sqrt(x3max*((s2/x3max)+(x3max*s3)));
+ }
+ }
+ }
+ return norm;
+}
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/MultidimensionalCounter.java b/src/main/java/org/apache/commons/math/util/MultidimensionalCounter.java
new file mode 100644
index 0000000..752fb3d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/MultidimensionalCounter.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.math.util;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+
+/**
+ * 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>
+ * <li>(0, 0, 1) corresponds to 1</li>
+ * <li>(0, 0, 2) corresponds to 2</li>
+ * <li>(0, 1, 0) corresponds to 3</li>
+ * <li>...</li>
+ * <li>(1, 0, 0) corresponds to 12</li>
+ * <li>...</li>
+ * <li>(1, 3, 2) corresponds to 23</li>
+ * </ul>
+ * @version $Revision$ $Date$
+ * @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;
+
+ /**
+ * Create an iterator
+ * @see #iterator()
+ */
+ Iterator() {
+ counter[last] = -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasNext() {
+ for (int i = 0; i < dimension; i++) {
+ if (counter[i] != size[i] - 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return the unidimensional count after the counter has been
+ * incremented by {@code 1}.
+ */
+ public Integer next() {
+ 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 /* Arrays.*/ copyOf(counter, dimension); // Java 1.5 does not support Arrays.copyOf()
+ }
+
+ /**
+ * 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) {
+ dimension = size.length;
+ this.size = /* Arrays.*/ copyOf(size, dimension); // Java 1.5 does not support Arrays.copyOf()
+
+ 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) {
+ 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;
+ }
+
+ int idx = 1;
+ while (count < index) {
+ count += idx;
+ ++idx;
+ }
+ --idx;
+ indices[last] = idx;
+
+ 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 {
+ 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 /* Arrays.*/ copyOf(size, dimension); // Java 1.5 does not support Arrays.copyOf()
+ }
+
+ /**
+ * {@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();
+ }
+
+ /**
+ * Java 1.5 does not support Arrays.copyOf()
+ *
+ * @param source the array to be copied
+ * @param newLen the length of the copy to be returned
+ * @return the copied array, truncated or padded as necessary.
+ */
+ private int[] copyOf(int[] source, int newLen) {
+ int[] output = new int[newLen];
+ System.arraycopy(source, 0, output, 0, Math.min(source.length, newLen));
+ return output;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/NumberTransformer.java b/src/main/java/org/apache/commons/math/util/NumberTransformer.java
new file mode 100644
index 0000000..07d04db
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/NumberTransformer.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.math.util;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * Subclasses implementing this interface can transform Objects to doubles.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ *
+ * 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 MathException if the Object can not be transformed into a Double.
+ */
+ double transform(Object o) throws MathException;
+}
diff --git a/src/main/java/org/apache/commons/math/util/OpenIntToDoubleHashMap.java b/src/main/java/org/apache/commons/math/util/OpenIntToDoubleHashMap.java
new file mode 100644
index 0000000..400735f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/OpenIntToDoubleHashMap.java
@@ -0,0 +1,600 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.util;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ * <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.</p>
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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. </p>
+ */
+ 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. </p>
+ */
+ 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.</p>
+ * @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) {
+ // 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+
+ // advance on step
+ current = next;
+
+ // prepare next step
+ try {
+ while (states[++next] != FULL) {
+ // nothing to do
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ next = -2;
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * 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/math/util/OpenIntToFieldHashMap.java b/src/main/java/org/apache/commons/math/util/OpenIntToFieldHashMap.java
new file mode 100644
index 0000000..297ab44
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/OpenIntToFieldHashMap.java
@@ -0,0 +1,620 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+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;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * 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>
+ * <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.</p>
+ * @param <T> the type of the field elements
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ * @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. </p>
+ */
+ 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. </p>
+ */
+ 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.</p>
+ * @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) {
+ // 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ 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 MathRuntimeException.createConcurrentModificationException(LocalizedFormats.MAP_MODIFIED_WHILE_ITERATING);
+ }
+
+ // advance on step
+ current = next;
+
+ // prepare next step
+ try {
+ while (states[++next] != FULL) {
+ // nothing to do
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ next = -2;
+ if (current < 0) {
+ throw MathRuntimeException.createNoSuchElementException(LocalizedFormats.ITERATOR_EXHAUSTED);
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * 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.getZero().getClass(), length);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/ResizableDoubleArray.java b/src/main/java/org/apache/commons/math/util/ResizableDoubleArray.java
new file mode 100644
index 0000000..b33f288
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/ResizableDoubleArray.java
@@ -0,0 +1,936 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * <p>
+ * A variable length {@link DoubleArray} implementation that automatically
+ * handles expanding and contracting its internal storage array as elements
+ * are added and removed.
+ * </p>
+ * <p>
+ * The internal storage array starts with capacity determined by the
+ * <code>initialCapacity</code> 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</code> and <code>expansionFactor</code> properties.
+ * 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.
+ * </p>
+ * <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</code> property) and if the difference
+ * is too large, the internal array is contracted to size
+ * <code>numElements + 1.</code> The determination of when the internal
+ * storage array is "too large" depends on the <code>expansionMode</code> and
+ * <code>contractionFactor</code> properties. 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>
+ * </p>
+ * <p>
+ * To avoid cycles of expansions and contractions, the
+ * <code>expansionFactor</code> must not exceed the
+ * <code>contractionFactor.</code> Constructors and mutators for both of these
+ * properties enforce this requirement, throwing IllegalArgumentException if it
+ * is violated.
+ * </p>
+ * @version $Revision: 1073474 $ $Date: 2011-02-22 20:52:17 +0100 (mar. 22 févr. 2011) $
+ */
+public class ResizableDoubleArray implements DoubleArray, Serializable {
+
+ /** additive expansion mode */
+ public static final int ADDITIVE_MODE = 1;
+
+ /** multiplicative expansion mode */
+ public static final int MULTIPLICATIVE_MODE = 0;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3485529955529426875L;
+
+ /**
+ * The contraction criteria determines when the internal array will be
+ * contracted to fit the number of elements contained in the element
+ * array + 1.
+ */
+ protected float contractionCriteria = 2.5f;
+
+ /**
+ * The expansion factor of the array. When the array needs to be expanded,
+ * the new array size will be
+ * <code>internalArray.length * expansionFactor</code>
+ * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
+ * <code>internalArray.length + expansionFactor</code> if
+ * <code>expansionMode</code> is set to ADDITIVE_MODE.
+ */
+ protected float expansionFactor = 2.0f;
+
+ /**
+ * Determines whether array expansion by <code>expansionFactor</code>
+ * is additive or multiplicative.
+ */
+ protected int expansionMode = MULTIPLICATIVE_MODE;
+
+ /**
+ * The initial capacity of the array. Initial capacity is not exposed as a
+ * property as it is only meaningful when passed to a constructor.
+ */
+ protected int initialCapacity = 16;
+
+ /**
+ * The internal storage array.
+ */
+ protected 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.
+ */
+ protected 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]
+ * </code>
+ */
+ protected int startIndex = 0;
+
+ /**
+ * Create a ResizableArray with default properties.
+ * <ul>
+ * <li><code>initialCapacity = 16</code></li>
+ * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
+ * <li><code>expansionFactor = 2.5</code></li>
+ * <li><code>contractionFactor = 2.0</code></li>
+ * </ul>
+ */
+ public ResizableDoubleArray() {
+ internalArray = new double[initialCapacity];
+ }
+
+ /**
+ * Create a ResizableArray with the specified initial capacity. Other
+ * properties take default values:
+ * <ul>
+ * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
+ * <li><code>expansionFactor = 2.5</code></li>
+ * <li><code>contractionFactor = 2.0</code></li>
+ * </ul>
+ * @param initialCapacity The initial size of the internal storage array
+ * @throws IllegalArgumentException if initialCapacity is not > 0
+ */
+ public ResizableDoubleArray(int initialCapacity) {
+ setInitialCapacity(initialCapacity);
+ internalArray = new double[this.initialCapacity];
+ }
+
+ /**
+ * Create a ResizableArray from an existing double[] with the
+ * initial capacity and numElements corresponding to the size of
+ * the supplied 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</code></li>
+ * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
+ * <li><code>expansionFactor = 2.5</code></li>
+ * <li><code>contractionFactor = 2.0</code></li>
+ * </ul>
+ *
+ * @param initialArray initial array
+ * @since 2.2
+ */
+ public ResizableDoubleArray(double[] initialArray) {
+ if (initialArray == null) {
+ this.internalArray = new double[initialCapacity];
+ } else {
+ this.internalArray = new double[initialArray.length];
+ System.arraycopy(initialArray, 0, this.internalArray, 0, initialArray.length);
+ initialCapacity = initialArray.length;
+ numElements = initialArray.length;
+ }
+ }
+
+ /**
+ * <p>
+ * Create a ResizableArray with the specified initial capacity
+ * and expansion factor. The remaining properties take default
+ * values:
+ * <ul>
+ * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
+ * <li><code>contractionFactor = 0.5 + expansionFactor</code></li>
+ * </ul></p>
+ * <p>
+ * Throws IllegalArgumentException if the following conditions are
+ * not met:
+ * <ul>
+ * <li><code>initialCapacity > 0</code></li>
+ * <li><code>expansionFactor > 1</code></li>
+ * </ul></p>
+ *
+ * @param initialCapacity The initial size of the internal storage array
+ * @param expansionFactor the array will be expanded based on this
+ * parameter
+ * @throws IllegalArgumentException if parameters are not valid
+ */
+ public ResizableDoubleArray(int initialCapacity, float expansionFactor) {
+ this.expansionFactor = expansionFactor;
+ setInitialCapacity(initialCapacity);
+ internalArray = new double[initialCapacity];
+ setContractionCriteria(expansionFactor +0.5f);
+ }
+
+ /**
+ * <p>
+ * Create a ResizableArray with the specified initialCapacity,
+ * expansionFactor, and contractionCriteria. The <code>expansionMode</code>
+ * will default to <code>MULTIPLICATIVE_MODE.</code></p>
+ * <p>
+ * Throws IllegalArgumentException if the following conditions are
+ * not met:
+ * <ul>
+ * <li><code>initialCapacity > 0</code></li>
+ * <li><code>expansionFactor > 1</code></li>
+ * <li><code>contractionFactor >= expansionFactor</code></li>
+ * </ul></p>
+ * @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.
+ * @throws IllegalArgumentException if parameters are not valid
+ */
+ public ResizableDoubleArray(int initialCapacity, float expansionFactor,
+ float contractionCriteria) {
+ this.expansionFactor = expansionFactor;
+ setContractionCriteria(contractionCriteria);
+ setInitialCapacity(initialCapacity);
+ internalArray = new double[initialCapacity];
+ }
+
+ /**
+ * <p>
+ * Create a ResizableArray with the specified properties.</p>
+ * <p>
+ * Throws IllegalArgumentException if the following conditions are
+ * not met:
+ * <ul>
+ * <li><code>initialCapacity > 0</code></li>
+ * <li><code>expansionFactor > 1</code></li>
+ * <li><code>contractionFactor >= expansionFactor</code></li>
+ * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
+ * </li>
+ * </ul></p>
+ *
+ * @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 IllegalArgumentException if parameters are not valid
+ */
+ public ResizableDoubleArray(int initialCapacity, float expansionFactor,
+ float contractionCriteria, int expansionMode) {
+ this.expansionFactor = expansionFactor;
+ setContractionCriteria(contractionCriteria);
+ setInitialCapacity(initialCapacity);
+ setExpansionMode(expansionMode);
+ internalArray = new double[initialCapacity];
+ }
+
+ /**
+ * 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 NullPointerException
+ * is thrown.
+ *
+ * @param original array to copy
+ * @since 2.0
+ */
+ public ResizableDoubleArray(ResizableDoubleArray original) {
+ copy(original, this);
+ }
+
+ /**
+ * Adds an element to the end of this expandable array.
+ *
+ * @param value to be added to end of array
+ */
+ public synchronized void addElement(double value) {
+ numElements++;
+ if ((startIndex + numElements) > internalArray.length) {
+ expand();
+ }
+ internalArray[startIndex + (numElements - 1)] = value;
+ if (shouldContract()) {
+ contract();
+ }
+ }
+
+ /**
+ * Adds several element to the end of this expandable array.
+ *
+ * @param 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;
+ }
+
+ /**
+ * <p>
+ * 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>
+ * <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.
+ * </p>
+ *
+ * @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
+ */
+ 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 criteria
+ 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), a MathRuntimeException is thrown.
+ *
+ * @param value new value to substitute for the most recently added value
+ * @return value that has been replaced in the array
+ * @since 2.0
+ */
+ public synchronized double substituteMostRecentElement(double value) {
+ if (numElements < 1) {
+ throw MathRuntimeException.createArrayIndexOutOfBoundsException(
+ LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
+ }
+
+ double discarded = internalArray[startIndex + (numElements - 1)];
+
+ internalArray[startIndex + (numElements - 1)] = value;
+
+ return discarded;
+ }
+
+
+ /**
+ * Checks the expansion factor and the contraction criteria 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 IllegalArgumentException if the contractionCriteria is less than
+ * the expansionCriteria.
+ */
+ protected void checkContractExpand(float contraction, float expansion) {
+
+ if (contraction < expansion) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
+ contraction, expansion);
+ }
+
+ if (contraction <= 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE,
+ contraction);
+ }
+
+ if (expansion <= 1.0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE,
+ expansion);
+ }
+ }
+
+ /**
+ * Clear the array, reset the size to the initialCapacity and the number
+ * of elements to zero.
+ */
+ public synchronized void clear() {
+ numElements = 0;
+ startIndex = 0;
+ internalArray = new double[initialCapacity];
+ }
+
+ /**
+ * 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() {
+ 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 IllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ public synchronized void discardFrontElements(int i) {
+
+ 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 IllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ public synchronized void discardMostRecentElements(int i) {
+
+ 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 IllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ private synchronized void discardExtremeElements(int i,boolean front) {
+ if (i > numElements) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY,
+ i, numElements);
+ } else if (i < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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>
+ * </p>
+ */
+ 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 == MULTIPLICATIVE_MODE) {
+ newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
+ } else {
+ newSize = internalArray.length + FastMath.round(expansionFactor);
+ }
+ 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) {
+ 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.
+ */
+ public float getContractionCriteria() {
+ return contractionCriteria;
+ }
+
+ /**
+ * 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 MathRuntimeException.createArrayIndexOutOfBoundsException(
+ LocalizedFormats.INDEX_LARGER_THAN_MAX,
+ index, numElements - 1);
+ } else if (index >= 0) {
+ return internalArray[startIndex + index];
+ } else {
+ throw MathRuntimeException.createArrayIndexOutOfBoundsException(
+ LocalizedFormats.CANNOT_RETRIEVE_AT_NEGATIVE_INDEX,
+ 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() {
+ 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
+ */
+ public float getExpansionFactor() {
+ return expansionFactor;
+ }
+
+ /**
+ * The <code>expansionMode</code> determines whether the internal storage
+ * array grows additively (ADDITIVE_MODE) or multiplicatively
+ * (MULTIPLICATIVE_MODE) when it is expanded.
+ *
+ * @return Returns the expansionMode.
+ */
+ public int getExpansionMode() {
+ return expansionMode;
+ }
+
+ /**
+ * 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.
+ */
+ synchronized int getInternalLength() {
+ 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 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
+ * @deprecated replaced by {@link #getInternalValues()} as of 2.0
+ */
+ @Deprecated
+ public synchronized double[] getValues() {
+ return internalArray;
+ }
+
+ /**
+ * 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
+ */
+ public synchronized double[] getInternalValues() {
+ return internalArray;
+ }
+
+ /**
+ * Sets the contraction criteria for this ExpandContractDoubleArray.
+ *
+ * @param contractionCriteria contraction criteria
+ */
+ public void setContractionCriteria(float contractionCriteria) {
+ checkContractExpand(contractionCriteria, getExpansionFactor());
+ synchronized(this) {
+ this.contractionCriteria = contractionCriteria;
+ }
+ }
+
+
+ /**
+ * 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.
+ */
+ public synchronized void setElement(int index, double value) {
+ if (index < 0) {
+ throw MathRuntimeException.createArrayIndexOutOfBoundsException(
+ LocalizedFormats.CANNOT_SET_AT_NEGATIVE_INDEX,
+ 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>
+ * <li><code>contractionFactor >= expansionFactor</code></li>
+ * </ul>
+ * @param expansionFactor the new expansion factor value.
+ * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
+ * than contractionFactor
+ */
+ public void setExpansionFactor(float expansionFactor) {
+ checkContractExpand(getContractionCriteria(), 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 IllegalArgumentException if the specified mode value is not valid
+ */
+ public void setExpansionMode(int expansionMode) {
+ if (expansionMode != MULTIPLICATIVE_MODE &&
+ expansionMode != ADDITIVE_MODE) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.UNSUPPORTED_EXPANSION_MODE,
+ expansionMode, MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE",
+ ADDITIVE_MODE, "ADDITIVE_MODE");
+ }
+ synchronized(this) {
+ this.expansionMode = expansionMode;
+ }
+ }
+
+ /**
+ * Sets the initial capacity. Should only be invoked by constructors.
+ *
+ * @param initialCapacity of the array
+ * @throws IllegalArgumentException if <code>initialCapacity</code> is not
+ * positive.
+ */
+ protected void setInitialCapacity(int initialCapacity) {
+ if (initialCapacity > 0) {
+ synchronized(this) {
+ this.initialCapacity = initialCapacity;
+ }
+ } else {
+ throw MathRuntimeException.createIllegalArgumentException(
+ LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE,
+ initialCapacity);
+ }
+ }
+
+ /**
+ * 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 IllegalArgumentException if <code>i</code> is negative.
+ */
+ public synchronized void setNumElements(int i) {
+
+ // If index is negative thrown an error
+ if (i < 0) {
+ throw MathRuntimeException.createIllegalArgumentException(
+ 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
+ if ((startIndex + i) > internalArray.length) {
+ expandTo(startIndex + i);
+ }
+
+ // 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 == MULTIPLICATIVE_MODE) {
+ return (internalArray.length / ((float) numElements)) > contractionCriteria;
+ } else {
+ return (internalArray.length - numElements) > contractionCriteria;
+ }
+ }
+
+ /**
+ * 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 starting index
+ */
+ public synchronized int start() {
+ return startIndex;
+ }
+
+ /**
+ * <p>Copies source to dest, copying the underlying data, so dest is
+ * a new, independent copy of source. Does not contract before
+ * the copy.</p>
+ *
+ * <p>Obtains synchronization locks on both source and dest
+ * (in that order) before performing the copy.</p>
+ *
+ * <p>Neither source nor dest may be null; otherwise a NullPointerException
+ * is thrown</p>
+ *
+ * @param source ResizableDoubleArray to copy
+ * @param dest ResizableArray to replace with a copy of the source array
+ * @since 2.0
+ *
+ */
+ public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) {
+ synchronized(source) {
+ synchronized(dest) {
+ dest.initialCapacity = source.initialCapacity;
+ dest.contractionCriteria = source.contractionCriteria;
+ 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() {
+ 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;
+ ResizableDoubleArray other = (ResizableDoubleArray) object;
+ result = result && (other.initialCapacity == initialCapacity);
+ result = result && (other.contractionCriteria == contractionCriteria);
+ 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 hash code representing this ResizableDoubleArray
+ * @since 2.0
+ */
+ @Override
+ public synchronized int hashCode() {
+ int[] hashData = new int[7];
+ hashData[0] = new Float(expansionFactor).hashCode();
+ hashData[1] = new Float(contractionCriteria).hashCode();
+ hashData[2] = expansionMode;
+ hashData[3] = Arrays.hashCode(internalArray);
+ hashData[4] = initialCapacity;
+ hashData[5] = numElements;
+ hashData[6] = startIndex;
+ return Arrays.hashCode(hashData);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math/util/TransformerMap.java b/src/main/java/org/apache/commons/math/util/TransformerMap.java
new file mode 100644
index 0000000..53d0b3a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/TransformerMap.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.math.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * 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.
+ * @version $Revision: 922713 $ $Date: 2010-03-14 02:26:13 +0100 (dim. 14 mars 2010) $
+ */
+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 MathException if the Object can not be transformed into a Double.
+ * @see org.apache.commons.math.util.NumberTransformer#transform(java.lang.Object)
+ */
+ public double transform(Object o) throws MathException {
+ 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/math/util/package.html b/src/main/java/org/apache/commons/math/util/package.html
new file mode 100644
index 0000000..407b17f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math/util/package.html
@@ -0,0 +1,20 @@
+<html>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ <!-- $Revision: 480440 $ $Date: 2006-11-29 08:14:12 +0100 (mer. 29 nov. 2006) $ -->
+ <body>Convenience routines and common data structures used throughout the commons-math library.</body>
+</html>