[commons-math] 01/06: Imported Upstream version 2.2
Markus Koschany
apo at moszumanska.debian.org
Fri Aug 26 14:33:48 UTC 2016
This is an automated email from the git hooks/post-receive script.
apo pushed a commit to branch master
in repository commons-math.
commit 7d1f80ad1e18d8bf2615504d0a40fba545101f48
Author: Markus Koschany <apo at debian.org>
Date: Fri Aug 26 16:12:24 2016 +0200
Imported Upstream version 2.2
---
LICENSE.txt | 376 ++
NOTICE.txt | 56 +
RELEASE-NOTES.txt | 157 +
build.xml | 358 ++
checkstyle.xml | 178 +
findbugs-exclude-filter.xml | 254 +
license-header.txt | 16 +
pom.xml | 364 ++
src/main/assembly/bin.xml | 52 +
src/main/assembly/src.xml | 39 +
.../math/ArgumentOutsideDomainException.java | 44 +
.../apache/commons/math/ConvergenceException.java | 99 +
.../apache/commons/math/ConvergingAlgorithm.java | 141 +
.../commons/math/ConvergingAlgorithmImpl.java | 155 +
.../commons/math/DimensionMismatchException.java | 67 +
.../math/DuplicateSampleAbscissaException.java | 51 +
src/main/java/org/apache/commons/math/Field.java | 51 +
.../java/org/apache/commons/math/FieldElement.java | 60 +
.../commons/math/FunctionEvaluationException.java | 211 +
.../commons/math/MathConfigurationException.java | 94 +
.../org/apache/commons/math/MathException.java | 217 +
.../apache/commons/math/MathRuntimeException.java | 717 +++
.../math/MaxEvaluationsExceededException.java | 84 +
.../math/MaxIterationsExceededException.java | 84 +
.../commons/math/analysis/BinaryFunction.java | 120 +
.../math/analysis/BivariateRealFunction.java | 40 +
.../commons/math/analysis/ComposableFunction.java | 506 ++
.../DifferentiableMultivariateRealFunction.java | 51 +
...ifferentiableMultivariateVectorialFunction.java | 36 +
.../DifferentiableUnivariateMatrixFunction.java | 35 +
.../DifferentiableUnivariateRealFunction.java | 34 +
.../DifferentiableUnivariateVectorialFunction.java | 35 +
.../math/analysis/MultivariateMatrixFunction.java | 40 +
.../math/analysis/MultivariateRealFunction.java | 39 +
.../analysis/MultivariateVectorialFunction.java | 39 +
.../math/analysis/TrivariateRealFunction.java | 40 +
.../math/analysis/UnivariateMatrixFunction.java | 37 +
.../math/analysis/UnivariateRealFunction.java | 36 +
.../math/analysis/UnivariateVectorialFunction.java | 37 +
.../integration/LegendreGaussIntegrator.java | 236 +
.../analysis/integration/RombergIntegrator.java | 121 +
.../analysis/integration/SimpsonIntegrator.java | 112 +
.../analysis/integration/TrapezoidIntegrator.java | 142 +
.../integration/UnivariateRealIntegrator.java | 106 +
.../integration/UnivariateRealIntegratorImpl.java | 183 +
.../commons/math/analysis/integration/package.html | 22 +
.../BicubicSplineInterpolatingFunction.java | 558 +++
.../interpolation/BicubicSplineInterpolator.java | 145 +
.../BivariateRealGridInterpolator.java | 44 +
.../DividedDifferenceInterpolator.java | 117 +
.../analysis/interpolation/LinearInterpolator.java | 75 +
.../analysis/interpolation/LoessInterpolator.java | 463 ++
.../MicrosphereInterpolatingFunction.java | 244 +
.../interpolation/MicrosphereInterpolator.java | 118 +
.../MultivariateRealInterpolator.java | 46 +
.../interpolation/NevilleInterpolator.java | 54 +
.../SmoothingBicubicSplineInterpolator.java | 178 +
...oothingPolynomialBicubicSplineInterpolator.java | 143 +
.../analysis/interpolation/SplineInterpolator.java | 127 +
.../TricubicSplineInterpolatingFunction.java | 483 ++
.../interpolation/TricubicSplineInterpolator.java | 198 +
.../TrivariateRealGridInterpolator.java | 49 +
.../interpolation/UnivariateRealInterpolator.java | 39 +
.../math/analysis/interpolation/package.html | 22 +
.../org/apache/commons/math/analysis/package.html | 33 +
.../analysis/polynomials/PolynomialFunction.java | 350 ++
.../PolynomialFunctionLagrangeForm.java | 305 ++
.../polynomials/PolynomialFunctionNewtonForm.java | 221 +
.../polynomials/PolynomialSplineFunction.java | 224 +
.../analysis/polynomials/PolynomialsUtils.java | 281 ++
.../commons/math/analysis/polynomials/package.html | 23 +
.../math/analysis/solvers/BisectionSolver.java | 133 +
.../commons/math/analysis/solvers/BrentSolver.java | 399 ++
.../math/analysis/solvers/LaguerreSolver.java | 434 ++
.../math/analysis/solvers/MullerSolver.java | 415 ++
.../math/analysis/solvers/NewtonSolver.java | 183 +
.../math/analysis/solvers/RiddersSolver.java | 247 +
.../math/analysis/solvers/SecantSolver.java | 230 +
.../analysis/solvers/UnivariateRealSolver.java | 165 +
.../solvers/UnivariateRealSolverFactory.java | 90 +
.../solvers/UnivariateRealSolverFactoryImpl.java | 64 +
.../analysis/solvers/UnivariateRealSolverImpl.java | 304 ++
.../solvers/UnivariateRealSolverUtils.java | 240 +
.../commons/math/analysis/solvers/package.html | 22 +
.../org/apache/commons/math/complex/Complex.java | 1007 ++++
.../apache/commons/math/complex/ComplexField.java | 78 +
.../apache/commons/math/complex/ComplexFormat.java | 383 ++
.../apache/commons/math/complex/ComplexUtils.java | 72 +
.../org/apache/commons/math/complex/package.html | 23 +
src/main/java/org/apache/commons/math/dfp/Dfp.java | 2399 ++++++++++
.../java/org/apache/commons/math/dfp/DfpDec.java | 369 ++
.../java/org/apache/commons/math/dfp/DfpField.java | 750 +++
.../java/org/apache/commons/math/dfp/DfpMath.java | 969 ++++
.../java/org/apache/commons/math/dfp/package.html | 88 +
.../AbstractContinuousDistribution.java | 231 +
.../math/distribution/AbstractDistribution.java | 69 +
.../distribution/AbstractIntegerDistribution.java | 319 ++
.../math/distribution/BetaDistribution.java | 65 +
.../math/distribution/BetaDistributionImpl.java | 284 ++
.../math/distribution/BinomialDistribution.java | 60 +
.../distribution/BinomialDistributionImpl.java | 279 ++
.../math/distribution/CauchyDistribution.java | 63 +
.../math/distribution/CauchyDistributionImpl.java | 320 ++
.../math/distribution/ChiSquaredDistribution.java | 53 +
.../distribution/ChiSquaredDistributionImpl.java | 324 ++
.../math/distribution/ContinuousDistribution.java | 43 +
.../math/distribution/DiscreteDistribution.java | 35 +
.../commons/math/distribution/Distribution.java | 56 +
.../math/distribution/ExponentialDistribution.java | 53 +
.../distribution/ExponentialDistributionImpl.java | 319 ++
.../commons/math/distribution/FDistribution.java | 60 +
.../math/distribution/FDistributionImpl.java | 360 ++
.../math/distribution/GammaDistribution.java | 67 +
.../math/distribution/GammaDistributionImpl.java | 355 ++
.../commons/math/distribution/HasDensity.java | 45 +
.../distribution/HypergeometricDistribution.java | 76 +
.../HypergeometricDistributionImpl.java | 421 ++
.../math/distribution/IntegerDistribution.java | 84 +
.../math/distribution/NormalDistribution.java | 65 +
.../math/distribution/NormalDistributionImpl.java | 349 ++
.../math/distribution/PascalDistribution.java | 73 +
.../math/distribution/PascalDistributionImpl.java | 276 ++
.../math/distribution/PoissonDistribution.java | 64 +
.../math/distribution/PoissonDistributionImpl.java | 343 ++
.../math/distribution/SaddlePointExpansion.java | 201 +
.../commons/math/distribution/TDistribution.java | 46 +
.../math/distribution/TDistributionImpl.java | 303 ++
.../math/distribution/WeibullDistribution.java | 66 +
.../math/distribution/WeibullDistributionImpl.java | 378 ++
.../math/distribution/ZipfDistribution.java | 71 +
.../math/distribution/ZipfDistributionImpl.java | 286 ++
.../apache/commons/math/distribution/package.html | 20 +
.../commons/math/estimation/AbstractEstimator.java | 318 ++
.../math/estimation/EstimatedParameter.java | 126 +
.../math/estimation/EstimationException.java | 61 +
.../commons/math/estimation/EstimationProblem.java | 68 +
.../apache/commons/math/estimation/Estimator.java | 90 +
.../math/estimation/GaussNewtonEstimator.java | 231 +
.../estimation/LevenbergMarquardtEstimator.java | 897 ++++
.../math/estimation/SimpleEstimationProblem.java | 111 +
.../math/estimation/WeightedMeasurement.java | 172 +
.../apache/commons/math/estimation/package.html | 25 +
.../math/exception/ConvergenceException.java | 61 +
.../math/exception/DimensionMismatchException.java | 53 +
.../exception/MathIllegalArgumentException.java | 112 +
.../math/exception/MathIllegalNumberException.java | 74 +
.../math/exception/MathIllegalStateException.java | 140 +
.../commons/math/exception/MathInternalError.java | 50 +
.../commons/math/exception/MathThrowable.java | 62 +
.../MathUnsupportedOperationException.java | 106 +
.../commons/math/exception/NoDataException.java | 47 +
.../exception/NonMonotonousSequenceException.java | 123 +
.../math/exception/NotPositiveException.java | 50 +
.../exception/NotStrictlyPositiveException.java | 50 +
.../math/exception/NullArgumentException.java | 50 +
.../math/exception/NumberIsTooLargeException.java | 89 +
.../math/exception/NumberIsTooSmallException.java | 90 +
.../math/exception/OutOfRangeException.java | 63 +
.../commons/math/exception/ZeroException.java | 48 +
.../org/apache/commons/math/exception/package.html | 23 +
.../commons/math/exception/util/ArgUtils.java | 58 +
.../math/exception/util/DummyLocalizable.java | 58 +
.../commons/math/exception/util/Localizable.java | 43 +
.../math/exception/util/LocalizedFormats.java | 345 ++
.../math/exception/util/MessageFactory.java | 78 +
.../commons/math/exception/util/package.html | 22 +
.../commons/math/fraction/AbstractFormat.java | 210 +
.../apache/commons/math/fraction/BigFraction.java | 1129 +++++
.../commons/math/fraction/BigFractionField.java | 78 +
.../commons/math/fraction/BigFractionFormat.java | 291 ++
.../org/apache/commons/math/fraction/Fraction.java | 655 +++
.../math/fraction/FractionConversionException.java | 56 +
.../commons/math/fraction/FractionField.java | 78 +
.../commons/math/fraction/FractionFormat.java | 274 ++
.../math/fraction/ProperBigFractionFormat.java | 239 +
.../math/fraction/ProperFractionFormat.java | 232 +
.../org/apache/commons/math/fraction/package.html | 22 +
.../math/genetics/AbstractListChromosome.java | 104 +
.../commons/math/genetics/BinaryChromosome.java | 92 +
.../commons/math/genetics/BinaryMutation.java | 52 +
.../apache/commons/math/genetics/Chromosome.java | 111 +
.../commons/math/genetics/ChromosomePair.java | 69 +
.../commons/math/genetics/CrossoverPolicy.java | 35 +
.../math/genetics/ElitisticListPopulation.java | 110 +
.../org/apache/commons/math/genetics/Fitness.java | 35 +
.../math/genetics/FixedGenerationCount.java | 70 +
.../commons/math/genetics/GeneticAlgorithm.java | 228 +
.../genetics/InvalidRepresentationException.java | 63 +
.../commons/math/genetics/ListPopulation.java | 155 +
.../commons/math/genetics/MutationPolicy.java | 33 +
.../commons/math/genetics/OnePointCrossover.java | 117 +
.../math/genetics/PermutationChromosome.java | 44 +
.../apache/commons/math/genetics/Population.java | 55 +
.../apache/commons/math/genetics/RandomKey.java | 290 ++
.../commons/math/genetics/RandomKeyMutation.java | 57 +
.../commons/math/genetics/SelectionPolicy.java | 32 +
.../commons/math/genetics/StoppingCondition.java | 35 +
.../commons/math/genetics/TournamentSelection.java | 114 +
.../org/apache/commons/math/genetics/package.html | 24 +
.../geometry/CardanEulerSingularityException.java | 45 +
.../math/geometry/NotARotationMatrixException.java | 60 +
.../org/apache/commons/math/geometry/Rotation.java | 1072 +++++
.../commons/math/geometry/RotationOrder.java | 175 +
.../org/apache/commons/math/geometry/Vector3D.java | 534 +++
.../commons/math/geometry/Vector3DFormat.java | 343 ++
.../org/apache/commons/math/geometry/package.html | 24 +
.../commons/math/linear/AbstractFieldMatrix.java | 1139 +++++
.../commons/math/linear/AbstractRealMatrix.java | 1071 +++++
.../commons/math/linear/AbstractRealVector.java | 932 ++++
.../org/apache/commons/math/linear/AnyMatrix.java | 48 +
.../commons/math/linear/Array2DRowFieldMatrix.java | 613 +++
.../commons/math/linear/Array2DRowRealMatrix.java | 621 +++
.../commons/math/linear/ArrayFieldVector.java | 869 ++++
.../commons/math/linear/ArrayRealVector.java | 1228 +++++
.../commons/math/linear/BiDiagonalTransformer.java | 381 ++
.../org/apache/commons/math/linear/BigMatrix.java | 331 ++
.../apache/commons/math/linear/BigMatrixImpl.java | 1505 ++++++
.../commons/math/linear/BlockFieldMatrix.java | 1670 +++++++
.../commons/math/linear/BlockRealMatrix.java | 1690 +++++++
.../commons/math/linear/CholeskyDecomposition.java | 71 +
.../math/linear/CholeskyDecompositionImpl.java | 356 ++
.../commons/math/linear/DecompositionSolver.java | 84 +
.../linear/DefaultFieldMatrixChangingVisitor.java | 62 +
.../DefaultFieldMatrixPreservingVisitor.java | 62 +
.../linear/DefaultRealMatrixChangingVisitor.java | 49 +
.../linear/DefaultRealMatrixPreservingVisitor.java | 49 +
.../commons/math/linear/EigenDecomposition.java | 137 +
.../math/linear/EigenDecompositionImpl.java | 619 +++
.../math/linear/FieldDecompositionSolver.java | 86 +
.../commons/math/linear/FieldLUDecomposition.java | 94 +
.../math/linear/FieldLUDecompositionImpl.java | 434 ++
.../apache/commons/math/linear/FieldMatrix.java | 798 +++
.../math/linear/FieldMatrixChangingVisitor.java | 63 +
.../math/linear/FieldMatrixPreservingVisitor.java | 62 +
.../apache/commons/math/linear/FieldVector.java | 358 ++
.../math/linear/InvalidMatrixException.java | 67 +
.../commons/math/linear/LUDecomposition.java | 92 +
.../commons/math/linear/LUDecompositionImpl.java | 423 ++
.../commons/math/linear/MatrixIndexException.java | 55 +
.../apache/commons/math/linear/MatrixUtils.java | 957 ++++
.../math/linear/MatrixVisitorException.java | 52 +
.../math/linear/NonSquareMatrixException.java | 42 +
.../linear/NotPositiveDefiniteMatrixException.java | 43 +
.../math/linear/NotSymmetricMatrixException.java | 43 +
.../commons/math/linear/OpenMapRealMatrix.java | 292 ++
.../commons/math/linear/OpenMapRealVector.java | 915 ++++
.../commons/math/linear/QRDecomposition.java | 77 +
.../commons/math/linear/QRDecompositionImpl.java | 453 ++
.../org/apache/commons/math/linear/RealMatrix.java | 871 ++++
.../math/linear/RealMatrixChangingVisitor.java | 62 +
.../apache/commons/math/linear/RealMatrixImpl.java | 629 +++
.../math/linear/RealMatrixPreservingVisitor.java | 61 +
.../org/apache/commons/math/linear/RealVector.java | 1005 ++++
.../commons/math/linear/RealVectorFormat.java | 341 ++
.../math/linear/SingularMatrixException.java | 40 +
.../math/linear/SingularValueDecomposition.java | 147 +
.../linear/SingularValueDecompositionImpl.java | 379 ++
.../commons/math/linear/SparseFieldMatrix.java | 190 +
.../commons/math/linear/SparseFieldVector.java | 657 +++
.../commons/math/linear/SparseRealMatrix.java | 29 +
.../commons/math/linear/SparseRealVector.java | 27 +
.../math/linear/TriDiagonalTransformer.java | 270 ++
.../org/apache/commons/math/linear/package.html | 20 +
.../commons/math/ode/AbstractIntegrator.java | 440 ++
.../commons/math/ode/ContinuousOutputModel.java | 379 ++
.../commons/math/ode/DerivativeException.java | 62 +
.../ExtendedFirstOrderDifferentialEquations.java | 66 +
.../commons/math/ode/FirstOrderConverter.java | 119 +
.../math/ode/FirstOrderDifferentialEquations.java | 66 +
.../commons/math/ode/FirstOrderIntegrator.java | 61 +
.../commons/math/ode/IntegratorException.java | 64 +
.../commons/math/ode/MultistepIntegrator.java | 412 ++
.../org/apache/commons/math/ode/ODEIntegrator.java | 138 +
.../math/ode/SecondOrderDifferentialEquations.java | 69 +
.../commons/math/ode/SecondOrderIntegrator.java | 60 +
.../math/ode/events/CombinedEventsManager.java | 249 +
.../commons/math/ode/events/EventException.java | 63 +
.../commons/math/ode/events/EventHandler.java | 183 +
.../apache/commons/math/ode/events/EventState.java | 431 ++
.../apache/commons/math/ode/events/package.html | 96 +
.../ode/jacobians/EventHandlerWithJacobians.java | 226 +
.../FirstOrderIntegratorWithJacobians.java | 899 ++++
.../math/ode/jacobians/ODEWithJacobians.java | 54 +
.../math/ode/jacobians/ParameterizedODE.java | 48 +
.../ode/jacobians/StepHandlerWithJacobians.java | 97 +
.../jacobians/StepInterpolatorWithJacobians.java | 188 +
.../apache/commons/math/ode/jacobians/package.html | 28 +
.../ode/nonstiff/AdamsBashforthIntegrator.java | 317 ++
.../commons/math/ode/nonstiff/AdamsIntegrator.java | 131 +
.../math/ode/nonstiff/AdamsMoultonIntegrator.java | 414 ++
.../ode/nonstiff/AdamsNordsieckTransformer.java | 312 ++
.../ode/nonstiff/AdaptiveStepsizeIntegrator.java | 349 ++
.../nonstiff/ClassicalRungeKuttaIntegrator.java | 75 +
.../ClassicalRungeKuttaStepInterpolator.java | 110 +
.../ode/nonstiff/DormandPrince54Integrator.java | 158 +
.../nonstiff/DormandPrince54StepInterpolator.java | 214 +
.../ode/nonstiff/DormandPrince853Integrator.java | 283 ++
.../nonstiff/DormandPrince853StepInterpolator.java | 480 ++
.../ode/nonstiff/EmbeddedRungeKuttaIntegrator.java | 379 ++
.../commons/math/ode/nonstiff/EulerIntegrator.java | 72 +
.../math/ode/nonstiff/EulerStepInterpolator.java | 91 +
.../commons/math/ode/nonstiff/GillIntegrator.java | 74 +
.../math/ode/nonstiff/GillStepInterpolator.java | 125 +
.../ode/nonstiff/GraggBulirschStoerIntegrator.java | 963 ++++
.../GraggBulirschStoerStepInterpolator.java | 401 ++
.../math/ode/nonstiff/HighamHall54Integrator.java | 132 +
.../ode/nonstiff/HighamHall54StepInterpolator.java | 103 +
.../math/ode/nonstiff/MidpointIntegrator.java | 68 +
.../ode/nonstiff/MidpointStepInterpolator.java | 100 +
.../math/ode/nonstiff/RungeKuttaIntegrator.java | 197 +
.../ode/nonstiff/RungeKuttaStepInterpolator.java | 183 +
.../math/ode/nonstiff/ThreeEighthesIntegrator.java | 72 +
.../nonstiff/ThreeEighthesStepInterpolator.java | 116 +
.../apache/commons/math/ode/nonstiff/package.html | 25 +
.../java/org/apache/commons/math/ode/package.html | 167 +
.../ode/sampling/AbstractStepInterpolator.java | 519 ++
.../math/ode/sampling/DummyStepHandler.java | 101 +
.../math/ode/sampling/DummyStepInterpolator.java | 150 +
.../math/ode/sampling/FixedStepHandler.java | 62 +
.../ode/sampling/NordsieckStepInterpolator.java | 291 ++
.../commons/math/ode/sampling/StepHandler.java | 78 +
.../math/ode/sampling/StepInterpolator.java | 132 +
.../commons/math/ode/sampling/StepNormalizer.java | 161 +
.../apache/commons/math/ode/sampling/package.html | 60 +
.../DifferentiableMultivariateRealOptimizer.java | 112 +
...fferentiableMultivariateVectorialOptimizer.java | 114 +
.../apache/commons/math/optimization/GoalType.java | 35 +
.../math/optimization/LeastSquaresConverter.java | 193 +
...artDifferentiableMultivariateRealOptimizer.java | 228 +
...fferentiableMultivariateVectorialOptimizer.java | 238 +
.../MultiStartMultivariateRealOptimizer.java | 216 +
.../MultiStartUnivariateRealOptimizer.java | 318 ++
.../optimization/MultivariateRealOptimizer.java | 101 +
.../math/optimization/OptimizationException.java | 68 +
.../math/optimization/RealConvergenceChecker.java | 55 +
.../math/optimization/RealPointValuePair.java | 88 +
.../math/optimization/SimpleRealPointChecker.java | 87 +
.../optimization/SimpleScalarValueChecker.java | 81 +
.../optimization/SimpleVectorialPointChecker.java | 90 +
.../optimization/SimpleVectorialValueChecker.java | 90 +
.../math/optimization/UnivariateRealOptimizer.java | 116 +
.../optimization/VectorialConvergenceChecker.java | 55 +
.../math/optimization/VectorialPointValuePair.java | 100 +
.../optimization/direct/DirectSearchOptimizer.java | 418 ++
.../math/optimization/direct/MultiDirectional.java | 144 +
.../math/optimization/direct/NelderMead.java | 181 +
.../math/optimization/direct/PowellOptimizer.java | 298 ++
.../commons/math/optimization/direct/package.html | 24 +
.../math/optimization/fitting/CurveFitter.java | 197 +
.../fitting/GaussianDerivativeFunction.java | 104 +
.../math/optimization/fitting/GaussianFitter.java | 117 +
.../optimization/fitting/GaussianFunction.java | 159 +
.../fitting/GaussianParametersGuesser.java | 271 ++
.../fitting/HarmonicCoefficientsGuesser.java | 300 ++
.../math/optimization/fitting/HarmonicFitter.java | 133 +
.../optimization/fitting/HarmonicFunction.java | 80 +
.../fitting/ParametricGaussianFunction.java | 165 +
.../fitting/ParametricRealFunction.java | 50 +
.../optimization/fitting/PolynomialFitter.java | 108 +
.../fitting/WeightedObservedPoint.java | 75 +
.../commons/math/optimization/fitting/package.html | 30 +
.../general/AbstractLeastSquaresOptimizer.java | 374 ++
.../AbstractScalarDifferentiableOptimizer.java | 207 +
.../general/ConjugateGradientFormula.java | 49 +
.../optimization/general/GaussNewtonOptimizer.java | 135 +
.../general/LevenbergMarquardtOptimizer.java | 888 ++++
.../NonLinearConjugateGradientOptimizer.java | 294 ++
.../math/optimization/general/Preconditioner.java | 52 +
.../commons/math/optimization/general/package.html | 22 +
.../linear/AbstractLinearOptimizer.java | 130 +
.../math/optimization/linear/LinearConstraint.java | 233 +
.../linear/LinearObjectiveFunction.java | 147 +
.../math/optimization/linear/LinearOptimizer.java | 89 +
.../linear/NoFeasibleSolutionException.java | 41 +
.../math/optimization/linear/Relationship.java | 67 +
.../math/optimization/linear/SimplexSolver.java | 185 +
.../math/optimization/linear/SimplexTableau.java | 589 +++
.../linear/UnboundedSolutionException.java | 41 +
.../commons/math/optimization/linear/package.html | 22 +
.../apache/commons/math/optimization/package.html | 72 +
.../AbstractUnivariateRealOptimizer.java | 275 ++
.../optimization/univariate/BracketFinder.java | 295 ++
.../optimization/univariate/BrentOptimizer.java | 227 +
.../math/optimization/univariate/package.html | 22 +
src/main/java/org/apache/commons/math/package.html | 20 +
.../math/random/AbstractRandomGenerator.java | 272 ++
.../apache/commons/math/random/AbstractWell.java | 186 +
.../commons/math/random/BitsStreamGenerator.java | 153 +
.../random/CorrelatedRandomVectorGenerator.java | 304 ++
.../commons/math/random/EmpiricalDistribution.java | 133 +
.../math/random/EmpiricalDistributionImpl.java | 479 ++
.../math/random/GaussianRandomGenerator.java | 47 +
.../commons/math/random/JDKRandomGenerator.java | 50 +
.../commons/math/random/MersenneTwister.java | 259 +
.../math/random/NormalizedRandomGenerator.java | 38 +
.../apache/commons/math/random/RandomAdaptor.java | 198 +
.../org/apache/commons/math/random/RandomData.java | 272 ++
.../apache/commons/math/random/RandomDataImpl.java | 966 ++++
.../commons/math/random/RandomGenerator.java | 148 +
.../commons/math/random/RandomVectorGenerator.java | 35 +
.../random/UncorrelatedRandomVectorGenerator.java | 93 +
.../math/random/UniformRandomGenerator.java | 61 +
.../random/UnitSphereRandomVectorGenerator.java | 84 +
.../apache/commons/math/random/ValueServer.java | 385 ++
.../org/apache/commons/math/random/Well1024a.java | 106 +
.../org/apache/commons/math/random/Well19937a.java | 108 +
.../org/apache/commons/math/random/Well19937c.java | 115 +
.../org/apache/commons/math/random/Well44497a.java | 111 +
.../org/apache/commons/math/random/Well44497b.java | 119 +
.../org/apache/commons/math/random/Well512a.java | 107 +
.../org/apache/commons/math/random/package.html | 132 +
.../java/org/apache/commons/math/special/Beta.java | 202 +
.../java/org/apache/commons/math/special/Erf.java | 92 +
.../org/apache/commons/math/special/Gamma.java | 339 ++
.../org/apache/commons/math/special/package.html | 20 +
.../org/apache/commons/math/stat/Frequency.java | 603 +++
.../org/apache/commons/math/stat/StatUtils.java | 663 +++
.../commons/math/stat/clustering/Cluster.java | 74 +
.../commons/math/stat/clustering/Clusterable.java | 46 +
.../stat/clustering/EuclideanIntegerPoint.java | 120 +
.../stat/clustering/KMeansPlusPlusClusterer.java | 333 ++
.../commons/math/stat/clustering/package.html | 20 +
.../commons/math/stat/correlation/Covariance.java | 274 ++
.../math/stat/correlation/PearsonsCorrelation.java | 285 ++
.../stat/correlation/SpearmansCorrelation.java | 172 +
.../commons/math/stat/correlation/package.html | 22 +
.../AbstractStorelessUnivariateStatistic.java | 183 +
.../descriptive/AbstractUnivariateStatistic.java | 232 +
.../descriptive/AggregateSummaryStatistics.java | 416 ++
.../stat/descriptive/DescriptiveStatistics.java | 721 +++
.../descriptive/MultivariateSummaryStatistics.java | 637 +++
.../StatisticalMultivariateSummary.java | 120 +
.../math/stat/descriptive/StatisticalSummary.java | 65 +
.../stat/descriptive/StatisticalSummaryValues.java | 186 +
.../descriptive/StorelessUnivariateStatistic.java | 86 +
.../math/stat/descriptive/SummaryStatistics.java | 717 +++
.../SynchronizedDescriptiveStatistics.java | 172 +
.../SynchronizedMultivariateSummaryStatistics.java | 299 ++
.../descriptive/SynchronizedSummaryStatistics.java | 333 ++
.../math/stat/descriptive/UnivariateStatistic.java | 53 +
.../math/stat/descriptive/WeightedEvaluation.java | 49 +
.../math/stat/descriptive/moment/FirstMoment.java | 160 +
.../math/stat/descriptive/moment/FourthMoment.java | 142 +
.../stat/descriptive/moment/GeometricMean.java | 205 +
.../math/stat/descriptive/moment/Kurtosis.java | 222 +
.../commons/math/stat/descriptive/moment/Mean.java | 272 ++
.../math/stat/descriptive/moment/SecondMoment.java | 124 +
.../math/stat/descriptive/moment/SemiVariance.java | 379 ++
.../math/stat/descriptive/moment/Skewness.java | 213 +
.../stat/descriptive/moment/StandardDeviation.java | 271 ++
.../math/stat/descriptive/moment/ThirdMoment.java | 139 +
.../math/stat/descriptive/moment/Variance.java | 610 +++
.../descriptive/moment/VectorialCovariance.java | 152 +
.../stat/descriptive/moment/VectorialMean.java | 103 +
.../math/stat/descriptive/moment/package.html | 20 +
.../commons/math/stat/descriptive/package.html | 41 +
.../commons/math/stat/descriptive/rank/Max.java | 163 +
.../commons/math/stat/descriptive/rank/Median.java | 55 +
.../commons/math/stat/descriptive/rank/Min.java | 163 +
.../math/stat/descriptive/rank/Percentile.java | 497 ++
.../math/stat/descriptive/rank/package.html | 20 +
.../math/stat/descriptive/summary/Product.java | 224 +
.../commons/math/stat/descriptive/summary/Sum.java | 220 +
.../math/stat/descriptive/summary/SumOfLogs.java | 165 +
.../stat/descriptive/summary/SumOfSquares.java | 154 +
.../math/stat/descriptive/summary/package.html | 20 +
.../commons/math/stat/inference/ChiSquareTest.java | 222 +
.../math/stat/inference/ChiSquareTestImpl.java | 424 ++
.../commons/math/stat/inference/OneWayAnova.java | 103 +
.../math/stat/inference/OneWayAnovaImpl.java | 210 +
.../apache/commons/math/stat/inference/TTest.java | 771 +++
.../commons/math/stat/inference/TTestImpl.java | 1069 +++++
.../commons/math/stat/inference/TestUtils.java | 436 ++
.../UnknownDistributionChiSquareTest.java | 144 +
.../commons/math/stat/inference/package.html | 23 +
.../java/org/apache/commons/math/stat/package.html | 20 +
.../commons/math/stat/ranking/NaNStrategy.java | 49 +
.../commons/math/stat/ranking/NaturalRanking.java | 464 ++
.../math/stat/ranking/RankingAlgorithm.java | 41 +
.../commons/math/stat/ranking/TiesStrategy.java | 55 +
.../apache/commons/math/stat/ranking/package.html | 22 +
.../AbstractMultipleLinearRegression.java | 366 ++
.../regression/GLSMultipleLinearRegression.java | 136 +
.../stat/regression/MultipleLinearRegression.java | 70 +
.../regression/OLSMultipleLinearRegression.java | 233 +
.../math/stat/regression/SimpleRegression.java | 639 +++
.../commons/math/stat/regression/package.html | 22 +
.../math/transform/FastCosineTransformer.java | 262 +
.../math/transform/FastFourierTransformer.java | 912 ++++
.../math/transform/FastHadamardTransformer.java | 252 +
.../math/transform/FastSineTransformer.java | 252 +
.../commons/math/transform/RealTransformer.java | 80 +
.../org/apache/commons/math/transform/package.html | 22 +
.../java/org/apache/commons/math/util/BigReal.java | 292 ++
.../org/apache/commons/math/util/BigRealField.java | 78 +
.../apache/commons/math/util/CompositeFormat.java | 220 +
.../commons/math/util/ContinuedFraction.java | 208 +
.../commons/math/util/DefaultTransformer.java | 80 +
.../org/apache/commons/math/util/DoubleArray.java | 104 +
.../org/apache/commons/math/util/FastMath.java | 4047 ++++++++++++++++
.../org/apache/commons/math/util/MathUtils.java | 2265 +++++++++
.../commons/math/util/MultidimensionalCounter.java | 316 ++
.../commons/math/util/NumberTransformer.java | 39 +
.../commons/math/util/OpenIntToDoubleHashMap.java | 600 +++
.../commons/math/util/OpenIntToFieldHashMap.java | 620 +++
.../commons/math/util/ResizableDoubleArray.java | 936 ++++
.../apache/commons/math/util/TransformerMap.java | 189 +
.../java/org/apache/commons/math/util/package.html | 20 +
.../localization/LocalizedFormats_fr.properties | 274 ++
src/main/resources/templates/math-release-notes.vm | 121 +
src/site/resources/images/math.gif | Bin 0 -> 15169 bytes
src/site/resources/style/project.css | 1 +
.../userguide/TrajectoryDeterminationProblem.java | 187 +
.../userguide/estimation-class-diagram.png | Bin 0 -> 20220 bytes
.../userguide/estimation-sequence-diagram.png | Bin 0 -> 36021 bytes
src/site/site.xml | 71 +
src/site/xdoc/changes.xml | 1312 +++++
src/site/xdoc/developers.xml | 311 ++
src/site/xdoc/download_math.xml | 138 +
src/site/xdoc/index.xml | 86 +
src/site/xdoc/issue-tracking.xml | 102 +
src/site/xdoc/mail-lists.xml | 202 +
src/site/xdoc/proposal.xml | 132 +
src/site/xdoc/userguide/analysis.xml | 413 ++
src/site/xdoc/userguide/complex.xml | 150 +
src/site/xdoc/userguide/distribution.xml | 95 +
src/site/xdoc/userguide/fraction.xml | 110 +
src/site/xdoc/userguide/genetics.xml | 135 +
src/site/xdoc/userguide/geometry.xml | 141 +
src/site/xdoc/userguide/index.xml | 147 +
src/site/xdoc/userguide/linear.xml | 205 +
src/site/xdoc/userguide/ode.xml | 440 ++
src/site/xdoc/userguide/optimization.xml | 271 ++
src/site/xdoc/userguide/overview.xml | 163 +
src/site/xdoc/userguide/random.xml | 538 +++
src/site/xdoc/userguide/special.xml | 70 +
src/site/xdoc/userguide/stat.xml | 1061 ++++
src/site/xdoc/userguide/transform.xml | 46 +
src/site/xdoc/userguide/utilities.xml | 195 +
src/site/xdoc/userguide/xdoc.xsl | 68 +
src/test/R/ChiSquareDistributionTestCases.R | 97 +
src/test/R/FDistributionTestCases.R | 92 +
src/test/R/GammaDistributionTestCases.R | 92 +
src/test/R/README.txt | 168 +
src/test/R/TDistributionTestCases.R | 100 +
src/test/R/TTestCases | 106 +
src/test/R/WeibullDistributionTestCases.R | 92 +
src/test/R/anovaTestCases | 72 +
src/test/R/binomialTestCases | 127 +
src/test/R/cauchyTestCases.R | 97 +
src/test/R/chiSquareTestCases | 101 +
src/test/R/correlationTestCases | 225 +
src/test/R/covarianceTestCases | 146 +
src/test/R/descriptiveTestCases | 83 +
src/test/R/exponentialTestCases | 103 +
src/test/R/hypergeometricTestCases | 136 +
src/test/R/multipleOLSRegressionTestCases | 309 ++
src/test/R/normalTestCases | 111 +
src/test/R/pascalTestCases | 135 +
src/test/R/poissonTestCases | 116 +
src/test/R/regressionTestCases | 159 +
src/test/R/testAll | 67 +
src/test/R/testFunctions | 86 +
.../math/ArgumentOutsideDomainExceptionTest.java | 40 +
.../commons/math/ConvergenceExceptionTest.java | 76 +
.../math/DuplicateSampleAbscissaExceptionTest.java | 38 +
.../math/FunctionEvaluationExceptionTest.java | 140 +
.../math/MathConfigurationExceptionTest.java | 75 +
.../org/apache/commons/math/MathExceptionTest.java | 142 +
.../math/MaxIterationsExceededExceptionTest.java | 53 +
.../org/apache/commons/math/RetryTestCase.java | 56 +
.../java/org/apache/commons/math/TestUtils.java | 516 ++
.../commons/math/analysis/BinaryFunctionTest.java | 77 +
.../math/analysis/ComposableFunctionTest.java | 147 +
.../commons/math/analysis/Expm1Function.java | 39 +
.../commons/math/analysis/MonitoredFunction.java | 48 +
.../commons/math/analysis/QuinticFunction.java | 40 +
.../apache/commons/math/analysis/SinFunction.java | 50 +
.../apache/commons/math/analysis/SincFunction.java | 50 +
.../commons/math/analysis/SumSincFunction.java | 84 +
.../integration/LegendreGaussIntegratorTest.java | 114 +
.../integration/RombergIntegratorTest.java | 114 +
.../integration/SimpsonIntegratorTest.java | 113 +
.../integration/TrapezoidIntegratorTest.java | 113 +
.../BicubicSplineInterpolatingFunctionTest.java | 448 ++
.../BicubicSplineInterpolatorTest.java | 170 +
.../DividedDifferenceInterpolatorTest.java | 143 +
.../interpolation/LinearInterpolatorTest.java | 148 +
.../interpolation/LoessInterpolatorTest.java | 255 +
.../interpolation/MicrosphereInterpolatorTest.java | 133 +
.../interpolation/NevilleInterpolatorTest.java | 144 +
.../SmoothingBicubicSplineInterpolatorTest.java | 179 +
...ingPolynomialBicubicSplineInterpolatorTest.java | 180 +
.../interpolation/SplineInterpolatorTest.java | 221 +
.../TricubicSplineInterpolatingFunctionTest.java | 546 +++
.../TricubicSplineInterpolatorTest.java | 212 +
.../PolynomialFunctionLagrangeFormTest.java | 150 +
.../PolynomialFunctionNewtonFormTest.java | 149 +
.../polynomials/PolynomialFunctionTest.java | 271 ++
.../polynomials/PolynomialSplineFunctionTest.java | 148 +
.../analysis/polynomials/PolynomialsUtilsTest.java | 218 +
.../math/analysis/solvers/BisectionSolverTest.java | 194 +
.../math/analysis/solvers/BrentSolverTest.java | 389 ++
.../math/analysis/solvers/LaguerreSolverTest.java | 199 +
.../math/analysis/solvers/MullerSolverTest.java | 271 ++
.../math/analysis/solvers/NewtonSolverTest.java | 109 +
.../math/analysis/solvers/RiddersSolverTest.java | 160 +
.../UnivariateRealSolverFactoryImplTest.java | 75 +
.../solvers/UnivariateRealSolverUtilsTest.java | 141 +
.../commons/math/complex/ComplexFieldTest.java | 44 +
.../math/complex/ComplexFormatAbstractTest.java | 362 ++
.../commons/math/complex/ComplexFormatTest.java | 33 +
.../apache/commons/math/complex/ComplexTest.java | 996 ++++
.../commons/math/complex/ComplexUtilsTest.java | 104 +
.../math/complex/FrenchComplexFormatTest.java | 34 +
.../org/apache/commons/math/dfp/Decimal10.java | 99 +
.../org/apache/commons/math/dfp/DfpDecTest.java | 565 +++
.../org/apache/commons/math/dfp/DfpMathTest.java | 587 +++
.../java/org/apache/commons/math/dfp/DfpTest.java | 1507 ++++++
.../math/distribution/BetaDistributionTest.java | 303 ++
.../distribution/BinomialDistributionTest.java | 130 +
.../math/distribution/CauchyDistributionTest.java | 129 +
.../distribution/ChiSquareDistributionTest.java | 148 +
.../ContinuousDistributionAbstractTest.java | 371 ++
.../distribution/ExponentialDistributionTest.java | 137 +
.../math/distribution/FDistributionTest.java | 150 +
.../math/distribution/GammaDistributionTest.java | 169 +
.../HypergeometricDistributionTest.java | 229 +
.../IntegerDistributionAbstractTest.java | 415 ++
.../math/distribution/NormalDistributionTest.java | 222 +
.../math/distribution/PascalDistributionTest.java | 136 +
.../math/distribution/PoissonDistributionTest.java | 231 +
.../math/distribution/TDistributionTest.java | 135 +
.../math/distribution/WeibullDistributionTest.java | 144 +
.../math/distribution/ZipfDistributionTest.java | 89 +
.../math/estimation/EstimatedParameterTest.java | 74 +
.../math/estimation/GaussNewtonEstimatorTest.java | 724 +++
.../LevenbergMarquardtEstimatorTest.java | 832 ++++
.../commons/math/estimation/MinpackTest.java | 1538 ++++++
.../math/estimation/WeightedMeasurementTest.java | 128 +
.../exception/DimensionMismatchExceptionTest.java | 34 +
.../NonMonotonousSequenceExceptionTest.java | 47 +
.../math/exception/NotPositiveExceptionTest.java | 35 +
.../NotStrictlyPositiveExceptionTest.java | 35 +
.../exception/NumberIsTooLargeExceptionTest.java | 35 +
.../exception/NumberIsTooSmallExceptionTest.java | 35 +
.../math/exception/OutOfRangeExceptionTest.java | 35 +
.../commons/math/exception/util/ArgUtilsTest.java | 76 +
.../math/exception/util/MessageFactoryTest.java | 57 +
.../math/fraction/BigFractionFieldTest.java | 44 +
.../math/fraction/BigFractionFormatTest.java | 323 ++
.../commons/math/fraction/BigFractionTest.java | 577 +++
.../commons/math/fraction/FractionFieldTest.java | 44 +
.../commons/math/fraction/FractionFormatTest.java | 303 ++
.../apache/commons/math/fraction/FractionTest.java | 583 +++
.../math/genetics/BinaryChromosomeTest.java | 67 +
.../commons/math/genetics/BinaryMutationTest.java | 44 +
.../commons/math/genetics/ChromosomeTest.java | 111 +
.../math/genetics/DummyBinaryChromosome.java | 44 +
.../commons/math/genetics/DummyRandomKey.java | 44 +
.../math/genetics/ElitisticListPopulationTest.java | 53 +
.../commons/math/genetics/FitnessCachingTest.java | 94 +
.../math/genetics/FixedGenerationCountTest.java | 63 +
.../math/genetics/GeneticAlgorithmTestBinary.java | 122 +
.../genetics/GeneticAlgorithmTestPermutations.java | 132 +
.../commons/math/genetics/ListPopulationTest.java | 61 +
.../math/genetics/OnePointCrossoverTest.java | 59 +
.../math/genetics/RandomKeyMutationTest.java | 44 +
.../commons/math/genetics/RandomKeyTest.java | 164 +
.../math/genetics/TournamentSelectionTest.java | 56 +
.../math/geometry/FrenchVector3DFormatTest.java | 34 +
.../commons/math/geometry/RotationOrderTest.java | 62 +
.../apache/commons/math/geometry/RotationTest.java | 488 ++
.../math/geometry/Vector3DFormatAbstractTest.java | 361 ++
.../commons/math/geometry/Vector3DFormatTest.java | 34 +
.../apache/commons/math/geometry/Vector3DTest.java | 226 +
.../math/linear/AbstractRealVectorTest.java | 244 +
.../math/linear/Array2DRowRealMatrixTest.java | 1008 ++++
.../commons/math/linear/ArrayFieldVectorTest.java | 632 +++
.../commons/math/linear/ArrayRealVectorTest.java | 1310 +++++
.../math/linear/BiDiagonalTransformerTest.java | 204 +
.../commons/math/linear/BigMatrixImplTest.java | 836 ++++
.../commons/math/linear/BlockFieldMatrixTest.java | 1288 +++++
.../commons/math/linear/BlockRealMatrixTest.java | 1204 +++++
.../math/linear/CholeskyDecompositionImplTest.java | 150 +
.../commons/math/linear/CholeskySolverTest.java | 119 +
.../math/linear/EigenDecompositionImplTest.java | 533 +++
.../commons/math/linear/EigenSolverTest.java | 158 +
.../math/linear/FieldLUDecompositionImplTest.java | 297 ++
.../commons/math/linear/FieldMatrixImplTest.java | 1005 ++++
.../math/linear/FrenchRealVectorFormatTest.java | 34 +
.../math/linear/InvalidMatrixExceptionTest.java | 32 +
.../math/linear/LUDecompositionImplTest.java | 288 ++
.../apache/commons/math/linear/LUSolverTest.java | 179 +
.../math/linear/MatrixIndexExceptionTest.java | 38 +
.../commons/math/linear/MatrixUtilsTest.java | 404 ++
.../math/linear/QRDecompositionImplTest.java | 256 +
.../apache/commons/math/linear/QRSolverTest.java | 213 +
.../commons/math/linear/RealMatrixImplTest.java | 1008 ++++
.../math/linear/RealVectorFormatAbstractTest.java | 387 ++
.../commons/math/linear/RealVectorFormatTest.java | 34 +
.../linear/SingularValueDecompositionImplTest.java | 250 +
.../math/linear/SingularValueSolverTest.java | 147 +
.../commons/math/linear/SparseFieldMatrixTest.java | 684 +++
.../commons/math/linear/SparseFieldVectorTest.java | 216 +
.../commons/math/linear/SparseRealMatrixTest.java | 671 +++
.../commons/math/linear/SparseRealVectorTest.java | 1194 +++++
.../math/linear/TriDiagonalTransformerTest.java | 175 +
.../math/ode/ContinuousOutputModelTest.java | 197 +
.../commons/math/ode/FirstOrderConverterTest.java | 113 +
.../org/apache/commons/math/ode/TestProblem1.java | 91 +
.../org/apache/commons/math/ode/TestProblem2.java | 93 +
.../org/apache/commons/math/ode/TestProblem3.java | 132 +
.../org/apache/commons/math/ode/TestProblem4.java | 159 +
.../org/apache/commons/math/ode/TestProblem5.java | 44 +
.../org/apache/commons/math/ode/TestProblem6.java | 93 +
.../commons/math/ode/TestProblemAbstract.java | 191 +
.../commons/math/ode/TestProblemFactory.java | 50 +
.../commons/math/ode/TestProblemHandler.java | 157 +
.../commons/math/ode/events/EventStateTest.java | 76 +
.../FirstOrderIntegratorWithJacobiansTest.java | 437 ++
.../ode/nonstiff/AdamsBashforthIntegratorTest.java | 157 +
.../ode/nonstiff/AdamsMoultonIntegratorTest.java | 157 +
.../ClassicalRungeKuttaIntegratorTest.java | 308 ++
.../ClassicalRungeKuttaStepInterpolatorTest.java | 94 +
.../nonstiff/DormandPrince54IntegratorTest.java | 363 ++
.../DormandPrince54StepInterpolatorTest.java | 152 +
.../nonstiff/DormandPrince853IntegratorTest.java | 428 ++
.../DormandPrince853StepInterpolatorTest.java | 152 +
.../math/ode/nonstiff/EulerIntegratorTest.java | 188 +
.../ode/nonstiff/EulerStepInterpolatorTest.java | 182 +
.../math/ode/nonstiff/GillIntegratorTest.java | 243 +
.../ode/nonstiff/GillStepInterpolatorTest.java | 95 +
.../nonstiff/GraggBulirschStoerIntegratorTest.java | 365 ++
.../GraggBulirschStoerStepInterpolatorTest.java | 154 +
.../ode/nonstiff/HighamHall54IntegratorTest.java | 355 ++
.../nonstiff/HighamHall54StepInterpolatorTest.java | 152 +
.../math/ode/nonstiff/MidpointIntegratorTest.java | 188 +
.../ode/nonstiff/MidpointStepInterpolatorTest.java | 95 +
.../commons/math/ode/nonstiff/StepProblem.java | 64 +
.../ode/nonstiff/ThreeEighthesIntegratorTest.java | 238 +
.../ThreeEighthesStepInterpolatorTest.java | 94 +
.../ode/sampling/DummyStepInterpolatorTest.java | 144 +
.../sampling/NordsieckStepInterpolatorTest.java | 92 +
.../ode/sampling/StepInterpolatorTestUtils.java | 90 +
.../math/ode/sampling/StepNormalizerTest.java | 119 +
...ifferentiableMultivariateRealOptimizerTest.java | 159 +
...entiableMultivariateVectorialOptimizerTest.java | 192 +
.../MultiStartMultivariateRealOptimizerTest.java | 81 +
.../MultiStartUnivariateRealOptimizerTest.java | 102 +
.../optimization/direct/MultiDirectionalTest.java | 245 +
.../math/optimization/direct/NelderMeadTest.java | 334 ++
.../optimization/direct/PowellOptimizerTest.java | 163 +
.../math/optimization/fitting/CurveFitterTest.java | 156 +
.../optimization/fitting/GaussianFitterTest.java | 321 ++
.../optimization/fitting/HarmonicFitterTest.java | 133 +
.../fitting/ParametricGaussianFunctionTest.java | 158 +
.../optimization/fitting/PolynomialFitterTest.java | 135 +
.../general/GaussNewtonOptimizerTest.java | 572 +++
.../general/LevenbergMarquardtOptimizerTest.java | 663 +++
.../math/optimization/general/MinpackTest.java | 1531 ++++++
.../NonLinearConjugateGradientOptimizerTest.java | 492 ++
.../optimization/linear/SimplexSolverTest.java | 458 ++
.../optimization/linear/SimplexTableauTest.java | 111 +
.../optimization/univariate/BracketFinderTest.java | 70 +
.../univariate/BrentOptimizerTest.java | 146 +
.../math/random/AbstractRandomGeneratorTest.java | 144 +
.../CorrelatedRandomVectorGeneratorTest.java | 148 +
.../math/random/EmpiricalDistributionTest.java | 259 +
.../math/random/GaussianRandomGeneratorTest.java | 43 +
.../commons/math/random/MersenneTwisterTest.java | 424 ++
.../commons/math/random/RandomAdaptorTest.java | 104 +
.../apache/commons/math/random/RandomDataTest.java | 990 ++++
.../commons/math/random/TestRandomGenerator.java | 42 +
.../UncorrelatedRandomVectorGeneratorTest.java | 84 +
.../math/random/UniformRandomGeneratorTest.java | 43 +
.../commons/math/random/ValueServerTest.java | 199 +
.../apache/commons/math/random/Well1024aTest.java | 200 +
.../apache/commons/math/random/Well19937aTest.java | 110 +
.../apache/commons/math/random/Well19937cTest.java | 110 +
.../apache/commons/math/random/Well44497aTest.java | 110 +
.../apache/commons/math/random/Well44497bTest.java | 110 +
.../apache/commons/math/random/Well512aTest.java | 71 +
.../org/apache/commons/math/special/BetaTest.java | 119 +
.../org/apache/commons/math/special/ErfTest.java | 190 +
.../org/apache/commons/math/special/GammaTest.java | 151 +
.../commons/math/stat/CertifiedDataTest.java | 154 +
.../apache/commons/math/stat/FrequencyTest.java | 287 ++
.../apache/commons/math/stat/StatUtilsTest.java | 468 ++
.../stat/clustering/EuclideanIntegerPointTest.java | 65 +
.../clustering/KMeansPlusPlusClustererTest.java | 169 +
.../math/stat/correlation/CovarianceTest.java | 236 +
.../stat/correlation/PearsonsCorrelationTest.java | 308 ++
.../correlation/SpearmansRankCorrelationTest.java | 130 +
.../math/stat/data/CertifiedDataAbstractTest.java | 156 +
.../org/apache/commons/math/stat/data/LewTest.java | 29 +
.../apache/commons/math/stat/data/LotteryTest.java | 29 +
.../AbstractUnivariateStatisticTest.java | 102 +
.../AggregateSummaryStatisticsTest.java | 290 ++
.../descriptive/DescriptiveStatisticsTest.java | 296 ++
.../math/stat/descriptive/InteractionTest.java | 88 +
.../math/stat/descriptive/ListUnivariateImpl.java | 213 +
.../stat/descriptive/ListUnivariateImplTest.java | 157 +
.../descriptive/MixedListUnivariateImplTest.java | 202 +
.../MultivariateSummaryStatisticsTest.java | 309 ++
.../descriptive/StatisticalSummaryValuesTest.java | 85 +
.../StorelessUnivariateStatisticAbstractTest.java | 211 +
.../stat/descriptive/SummaryStatisticsTest.java | 299 ++
.../SynchronizedDescriptiveStatisticsTest.java | 32 +
...chronizedMultivariateSummaryStatisticsTest.java | 33 +
.../SynchronizedSummaryStatisticsTest.java | 31 +
.../UnivariateStatisticAbstractTest.java | 181 +
.../stat/descriptive/moment/FirstMomentTest.java | 54 +
.../stat/descriptive/moment/FourthMomentTest.java | 54 +
.../stat/descriptive/moment/GeometricMeanTest.java | 83 +
.../math/stat/descriptive/moment/KurtosisTest.java | 70 +
.../math/stat/descriptive/moment/MeanTest.java | 71 +
.../stat/descriptive/moment/SecondMomentTest.java | 54 +
.../stat/descriptive/moment/SemiVarianceTest.java | 142 +
.../math/stat/descriptive/moment/SkewnessTest.java | 69 +
.../descriptive/moment/StandardDeviationTest.java | 99 +
.../stat/descriptive/moment/ThirdMomentTest.java | 54 +
.../math/stat/descriptive/moment/VarianceTest.java | 120 +
.../moment/VectorialCovarianceTest.java | 101 +
.../stat/descriptive/moment/VectorialMeanTest.java | 95 +
.../math/stat/descriptive/rank/MaxTest.java | 78 +
.../math/stat/descriptive/rank/MedianTest.java | 53 +
.../math/stat/descriptive/rank/MinTest.java | 78 +
.../math/stat/descriptive/rank/PercentileTest.java | 159 +
.../math/stat/descriptive/summary/ProductTest.java | 87 +
.../math/stat/descriptive/summary/SumLogTest.java | 83 +
.../math/stat/descriptive/summary/SumSqTest.java | 69 +
.../math/stat/descriptive/summary/SumTest.java | 77 +
.../math/stat/inference/ChiSquareFactoryTest.java | 37 +
.../math/stat/inference/ChiSquareTestTest.java | 251 +
.../math/stat/inference/OneWayAnovaTest.java | 120 +
.../math/stat/inference/TTestFactoryTest.java | 37 +
.../commons/math/stat/inference/TTestTest.java | 291 ++
.../commons/math/stat/inference/TestUtilsTest.java | 458 ++
.../math/stat/ranking/NaturalRankingTest.java | 204 +
.../GLSMultipleLinearRegressionTest.java | 297 ++
.../MultipleLinearRegressionAbstractTest.java | 136 +
.../OLSMultipleLinearRegressionTest.java | 512 ++
.../math/stat/regression/SimpleRegressionTest.java | 363 ++
.../math/transform/FastCosineTransformerTest.java | 121 +
.../math/transform/FastFourierTransformerTest.java | 174 +
.../transform/FastHadamardTransformerTest.java | 119 +
.../math/transform/FastSineTransformerTest.java | 120 +
.../apache/commons/math/util/BigRealFieldTest.java | 44 +
.../org/apache/commons/math/util/BigRealTest.java | 128 +
.../commons/math/util/ContinuedFractionTest.java | 56 +
.../commons/math/util/DefaultTransformerTest.java | 102 +
.../commons/math/util/DoubleArrayAbstractTest.java | 132 +
.../math/util/FastMathStrictComparisonTest.java | 248 +
.../org/apache/commons/math/util/FastMathTest.java | 1118 +++++
.../commons/math/util/FastMathTestPerformance.java | 377 ++
.../apache/commons/math/util/MathUtilsTest.java | 1640 +++++++
.../math/util/MultidimensionalCounterTest.java | 169 +
.../math/util/OpenIntToDoubleHashMapTest.java | 304 ++
.../commons/math/util/OpenIntToFieldTest.java | 315 ++
.../math/util/ResizableDoubleArrayTest.java | 540 +++
.../org/apache/commons/math/util/TestBean.java | 72 +
.../commons/math/util/TransformerMapTest.java | 114 +
.../org/apache/commons/math/random/emptyFile.txt | 0
.../org/apache/commons/math/random/testData.txt | 1000 ++++
.../org/apache/commons/math/stat/data/Lew.txt | 252 +
.../org/apache/commons/math/stat/data/Lottery.txt | 261 +
.../org/apache/commons/math/stat/data/Mavro.txt | 110 +
.../org/apache/commons/math/stat/data/Michelso.txt | 160 +
.../org/apache/commons/math/stat/data/NumAcc1.txt | 63 +
.../org/apache/commons/math/stat/data/NumAcc2.txt | 1061 ++++
.../org/apache/commons/math/stat/data/NumAcc3.txt | 1061 ++++
.../org/apache/commons/math/stat/data/NumAcc4.txt | 1061 ++++
.../org/apache/commons/math/stat/data/PiDigits.txt | 5060 ++++++++++++++++++++
test-jar.xml | 130 +
875 files changed, 207210 insertions(+)
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..275e288
--- /dev/null
+++ b/LICENSE.txt
@@ -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.txt b/NOTICE.txt
new file mode 100644
index 0000000..977df71
--- /dev/null
+++ b/NOTICE.txt
@@ -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/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
new file mode 100644
index 0000000..81b237c
--- /dev/null
+++ b/RELEASE-NOTES.txt
@@ -0,0 +1,157 @@
+
+
+ Apache Commons Math 2.2 RELEASE NOTES
+
+This is primarily a maintenance release, but it also includes new features and enhancements.
+
+Users of version 2.1 are encouraged to upgrade to 2.2, as this release includes some important bug fixes.
+
+See the detailed list of changes below for full description of all bug fixes and enhancements.
+
+This release contains some minor compatibility breaks with version 2.1 in some internal classes but none
+ of them are in APIs likely to be accessed by user code:
+ the MessagesResources_fr class has been removed (replaced by a properties file);
+ the arguments of the EventState.reinitializeBegin method have changed;
+ some protected fields which already had public accessors in AbstractStepInterpolator have been replaced.
+
+There is a behavior change that users of the multiple regression classes should be aware of. In version
+2.1, there was no way to estimate models without intercept terms, and, while this was not clear from
+the documentation, design (X) matrices needed to include initial unitary columns. In 2.2, initial
+unitary columns are not necessary and whether or not models include intercept terms is configurable.
+See the change log and javadoc for the classes in org.apache.commons.math.stat.regression for details.
+
+The major new features are:
+ a new FastMath class, both faster, more accurate and with a few additional functions than StrictMath and Math;
+ a new package for floating point arbitrary precision computing, including high level functions like exponential, sine, square root ...;
+ new linear and tricubic interpolators;
+ a new Gaussian curve fitter;
+ a new erfc function;
+ characteristic support for distributions;
+ a set of new Well Equidistributed Long-period Linear (WELL) random generators.
+
+Changes in this version include:
+
+New features:
+o MATH-364: Added complementary error function, erfc. Thanks to Christian Winter.
+o MATH-385: Added characteristic support to distributions, including methods to return numerical
+ estimates of the mean and variance and upper and lower bounds of support. In version 2.2,
+ methods returning distribution characteristics have been added only to the implementation
+ classes. In version 3, supporting methods have been added to the abstract base classes
+ and distribution interfaces.
+o MATH-440: Created "MathUserException" class to convey cause of failure between
+ layers of user code separated by a layer of Commons-Math code.
+o MATH-419: Added new random number generators from the Well Equidistributed Long-period Linear (WELL).
+o MATH-412: Added the dfp library providing arbitrary precision floating point computation in the spirit of
+ IEEE 854-1987 (not exactly as it uses base 1000 instead of base 10). In addition to finite numbers,
+ infinities and NaNs are available (but there are no subnormals). All IEEE 854-1987 rounding modes and
+ signaling flags are supported. The available operations are +, -, *, / and the available functions
+ are sqrt, sin, cos, tan, asin, acos, atan, exp, log. Thanks to Bill Rossi.
+o MATH-375: Added faster and more accurate version of traditional mathematical functions in a FastMath
+ class intended to be a drop-in replacement for java.util.Math at source-level. Some functions
+ still directly delegates to Math but this will improve with time. Some functions like exp
+ may be twice as fast (even 3 times faster on some processors). Sine, cosine or power functions
+ show typical speedups of 1.5 times faster or more. Thanks to Bill Rossi.
+o MATH-400: Added support for Gaussian curve fitting. Thanks to J. Lewis Muir.
+o MATH-388: Added a feature allowing error estimation to be computed only on a subset of
+ Ordinary Differential Equations, considered as the main set, the remaining equations
+ being considered only as an extension set that should not influence the ODE integration
+ algorithm
+o MATH-379: Created "MultidimensionalCounter" class.
+o MATH-378: Implementation of linear interpolation. Thanks to Matthew Rowles.
+o MATH-370: Added new "equalsIncludingNaN" methods that have the same semantics as the old "equals" methods.
+ The semantics of the old methods will be modified (in the next major release) such that
+ NaNs are not considered equal (to be more compliant with IEEE754).
+o MATH-366: Implementation of tricubic interpolation.
+
+Fixed Bugs:
+o MATH-505: TestUtils is thread-hostile. Deprecate the getters and setters.
+o MATH-471: MathUtils.equals(double, double) does not work properly for floats
+ - add equivalent (float, float) methods and basic tests
+o MATH-467: Fixed an awkward statement that triggered a false positive warning.
+o MATH-456: Modified erf (and erfc) to return extreme values for x with abs(x) > 40.
+ For these arguments, the true value is indistinguishable from an extrema as a double.
+o MATH-414: Modified NormalDistributionImpl.cumulativeProbability to return 0 or 1,
+ respectively for values more than 40 standard deviations from the mean.
+ For these values, the actual probability is indistinguishable from 0 or 1
+ as a double. Top coding improves performance for extreme values and prevents
+ convergence exceptions.
+o MATH-380: Deprecated the whole ode.jacobians package. It is clumsy and difficult to use. It will
+ be replaced by a completely rewritten implementation in 3.0, which will be more tightly
+ bound to the top level ode package
+o MATH-426: Added a normalization feature to transform samples so they have zero mean and unit standard deviation Thanks to Erik van Ingen.
+o MATH-429: Fixed k-means++ to add several strategies to deal with empty clusters that may appear
+ during iterations
+o MATH-391: Fixed an error preventing zero length vectors to be built by some constructors
+o MATH-421: Fixed an error preventing ODE solvers to be restarted after they have been stopped by a discrete event
+o MATH-415: Fixed lost cause in MathRuntimeException.createInternalError. Note that the message is still the default
+ message for internal errors asking to report a bug to commons-math JIRA tracker. In order to retrieve
+ the message from the root cause, one has to get the cause itself by getCause().
+o MATH-411: Modified multiple regression newSample methods to ensure that by default in all cases,
+ regression models are estimated with intercept terms. Prior to the fix for this issue,
+ newXSampleData(double[][]), newSampleData(double[], double[][]) and
+ newSampleData(double[], double[][], double[][]) all required columns of "1's" to be inserted
+ into the x[][] arrays to create a model with an intercept term; while newSampleData(double[], int, int)
+ created a model including an intercept term without requiring the unitary column. All methods have
+ been changed to eliminate the need for users to add unitary columns to specify regression models.
+ Users of OLSMultipleLinearRegression or GLSMultipleLinearRegression versions 2.0 or 2.1 should either
+ verify that their code either does not use the first set of data loading methods above or set the noIntercept
+ property to true on estimated models to get the previous behavior.
+o MATH-386: Added R-squared and adjusted R-squared statistics to OLSMultipleLinearRegression.
+o MATH-392: Corrected the formula used for Y variance returned by calculateYVariance and associated
+ methods in multiple regression classes (AbstractMultipleLinearRegression,
+ OLSMultipleLinearRegression, GLSMultipleLinearRegression). These methods previously returned
+ estimates of the variance in the model error term. New "calulateErrorVariance" methods have
+ been added to compute what was previously returned by calculateYVariance. Thanks to Mark Devaney.
+o MATH-406: Bug fixed in Levenberg-Marquardt (handling of weights).
+o MATH-405: Bug fixed in Levenberg-Marquardt (consistency of current).
+o MATH-377: Bug fixed in chi-square computation in AbstractLeastSquaresOptimizer.
+o MATH-395: Fixed several bugs in "BrentOptimizer".
+o MATH-393: Fixed inconsistency in return values in "MultiStartUnivariateRealOptimizer".
+o MATH-382: Fixed bug in precondition check (method "setMicrosphereElements").
+o MATH-361: Improved localization of error messages.
+o MATH-376: Allow multiple optimizations with a default simplex.
+o MATH-352: Added a setQRRankingThreshold method to Levenberg-Marquardt optimizer to improve robustness
+ of rank determination.
+o MATH-362: Fixed Levenberg-Marquardt optimizer that did not use the vectorial convergence checker.
+ Now this optimizer can use either the general vectorial convergence checker or its own
+ specialized convergence settings.
+o MATH-371: Fixed loss of significance error in PersonsCorrelation p-value computation causing p-values
+ smaller than the machine epsilon (~1E-16) to be reported as 0. Thanks to Kevin Childs.
+o MATH-369: Fix NullPointerException in BisectionSolver.solve(f, min, max, initial) Thanks to Sasun Pundev.
+o MATH-368: Fix spelling of getSparcity [sic] method of OpenMapRealVector
+o MATH-367: Fix problem with the default sparseIterator when a RealVector has exactly one non-zero entry Thanks to Albert Huang.
+
+Changes:
+o MATH-384: Added a constructor and addValues(double[]) methods allowing DescriptiveStatistics to
+ be initialized with values from a double[] array. Similarly enhanced
+ ResizeableDoubleArray.
+o MATH-448: Added a getUniqueCount() method to Frequency to return the number of unique
+ values included in the frequency table. Thanks to Patrick Meyer.
+o MATH-420: Added toString() override to StatisticalSummaryValues.
+o MATH-417: Improved Percentile performance by using a selection algorithm instead of a
+ complete sort, and by allowing caching data array and pivots when several
+ different percentiles are desired
+o MATH-409: Made intercept / no intercept configurable in multiple regression classes. By default, regression
+ models are estimated with an intercept term. When the "noIntercept" property is set to
+ true, regression models are estimated without intercepts.
+o MATH-361: Created package "exception" to contain the new exceptions hierarchy.
+ Created package "exception.util": utilities for the exception classes
+ (e.g. managing the localization of error messages).
+ Default policy for dealing with invalid null references: raise a
+ "NullArgumentException" (subclass of "IllegalArgumentException").
+o MATH-310: Added random data generation methods to RandomDataImpl for the remaining distributions in the
+ distributions package. Added a generic nextInversionDeviate method that takes a discrete
+ or continuous distribution as argument and generates a random deviate from the distribution.
+ Also added sampling methods based on the implementations in RandomDataImpl to distributions.
+o MATH-365: Deprecated SmoothingBicubicSplineInterpolator and SmoothingBicubicSplineInterpolatorTest.
+ Added BicubicSplineInterpolator and BicubicSplineInterpolatorTest.
+ Added SmoothingPolynomialBicubicSplineInterpolator and SmoothingPolynomialBicubicSplineInterpolatorTest.
+ Added method to clear the list of observations in PolynomialFitter.
+
+
+For complete information on Commons Math, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache Commons Math website:
+
+http://commons.apache.org/math/
+
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..13c8389
--- /dev/null
+++ b/build.xml
@@ -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.
+-->
+<project name="Commons Math" default="jar" basedir=".">
+
+
+<!--
+ "Math" component of the Apache Commons Project
+ $Id: build.xml 1072977 2011-02-21 14:22:47Z sebb $
+ $Revision: 1072977 $ $Date: 2011-02-21 15:22:47 +0100 (lun. 21 févr. 2011) $
+-->
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+ <property file="build.properties"/> <!-- Component local -->
+ <property file="${user.home}/build.properties"/> <!-- User local -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+ <!-- Junit -->
+ <property name="junit.version" value="4.8.2"/>
+ <property name="junit.home" value="/usr/share/junit"/>
+ <property name="junit.jar" value="${junit.home}/junit-${junit.version}.jar"/>
+
+
+<!-- ========== Component Declarations ==================================== -->
+
+
+ <!-- The name of this component -->
+ <property name="component.name" value="commons-math"/>
+
+ <!-- The primary package name of this component -->
+ <property name="component.package" value="org.apache.commons.math"/>
+
+ <!-- The title of this component -->
+ <property name="component.title" value="Commons MATH"/>
+
+ <!-- The current version number of this component -->
+ <property name="component.version" value="2.2"/>
+
+ <!-- The base directory for component sources -->
+ <property name="source.home" value="src/main/java"/>
+
+ <!-- The base directory for unit test sources -->
+ <property name="test.home" value="src/test/java"/>
+
+ <!-- The base directory for unit test resources -->
+ <property name="test.resources" value="src/test/resources"/>
+
+ <!-- Download lib dir -->
+ <property name="download.lib.dir" value="lib"/>
+
+ <!-- The base directory for compilation targets -->
+ <property name="build.home" value="target"/>
+
+ <!-- The base directory for distribution targets -->
+ <property name="dist.home" value="${build.home}/dist"/>
+
+ <!-- The base directory for test reports -->
+ <property name="test.reports" value="${build.home}/test-reports"/>
+
+ <!-- Base file name for dist files -->
+ <property name="final.name" value="${component.name}-${component.version}"/>
+
+ <!-- Directory where binary release files are staged -->
+ <property name="stage.bin.dir" value="${dist.home}/stage-bin"/>
+
+ <!-- Directory where source release files are staged -->
+ <property name="stage.src.dir" value="${dist.home}/stage-src"/>
+
+<!-- ========== Compiler Defaults ========================================= -->
+
+ <!-- Should Java compilations set the 'debug' compiler option? -->
+ <property name="compile.debug" value="true"/>
+
+ <!-- Should Java compilations set the 'deprecation' compiler option? -->
+ <property name="compile.deprecation" value="false"/>
+
+ <!-- Should Java compilations set the 'optimize' compiler option? -->
+ <property name="compile.optimize" value="true"/>
+
+ <!-- JDK level -->
+ <property name="compile.source" value="1.5"/>
+ <property name="compile.target" value="1.5"/>
+
+ <!-- Base compile classpath -->
+ <path id="compile.classpath">
+ <pathelement location="${build.home}/classes"/>
+ </path>
+
+ <!-- External dependency classpath -->
+ <path id="downloaded.lib.classpath">
+ <pathelement location="${download.lib.dir}/junit-${junit.version}.jar"/>
+ </path>
+
+<!-- ========== Test Execution Defaults =================================== -->
+
+
+ <!-- Construct unit test classpath -->
+ <path id="test.classpath">
+ <pathelement location="${build.home}/classes"/>
+ <pathelement location="${build.home}/test-classes"/>
+ <pathelement location="${junit.jar}"/>
+ <path refid="downloaded.lib.classpath"/>
+ </path>
+
+ <!-- Should the build fail if there are test failures? -->
+ <property name="test.failonerror" value="true"/>
+
+<!-- ========== Executable Targets ======================================== -->
+
+ <target name="clean" description="Clean build and distribution directories">
+ <delete dir="${build.home}"/>
+ </target>
+
+
+ <target name="init"
+ description="Initialize and evaluate conditionals">
+ <echo message="-------- ${component.title} ${component.version} --------"/>
+ <filter token="name" value="${component.name}"/>
+ <filter token="package" value="${component.package}"/>
+ <filter token="version" value="${component.version}"/>
+ <filter token="compilesource" value="${compile.source}"/>
+ <filter token="compiletarget" value="${compile.target}"/>
+ <tstamp/>
+ <mkdir dir="${build.home}"/>
+ <mkdir dir="${build.home}/classes"/>
+ <mkdir dir="${build.home}/test-classes"/>
+ <mkdir dir="${build.home}/classes/META-INF"/>
+ <copy todir="${build.home}/classes/META-INF">
+ <fileset dir="src/main/resources/META-INF" />
+ </copy>
+ </target>
+
+<!-- ========== Compile Targets =========================================== -->
+
+ <target name="compile" depends="init" description="Compile">
+
+ <javac srcdir="${source.home}"
+ destdir="${build.home}/classes"
+ source="${compile.source}"
+ target="${compile.target}"
+ debug="${compile.debug}"
+ deprecation="${compile.deprecation}"
+ includeantruntime="false"
+ optimize="${compile.optimize}">
+ <classpath refid="compile.classpath"/>
+ </javac>
+ </target>
+
+
+<!-- ========== Unit Test Targets ========================================= -->
+
+ <target name="compile.tests" depends="compile, download-dependencies" description="Compile unit tests.">
+
+ <javac srcdir="${test.home}"
+ destdir="${build.home}/test-classes"
+ debug="${compile.debug}"
+ deprecation="${compile.deprecation}"
+ includeantruntime="false"
+ optimize="${compile.optimize}">
+ <classpath refid="test.classpath"/>
+ </javac>
+
+ <copy todir="${build.home}/test-classes">
+ <fileset dir="${test.resources}">
+ <include name="**/*.xml"/>
+ <include name="**/*.txt"/>
+ </fileset>
+ </copy>
+
+ </target>
+
+ <target name="test" depends="compile.tests"
+ description="Run unit tests">
+ <mkdir dir="${test.reports}"/>
+ <junit printsummary="true"
+ errorProperty="test.failed"
+ failureProperty="test.failed"
+ fork="true"
+ showOutput="true">
+ <formatter type="brief"/>
+ <classpath refid="test.classpath"/>
+ <!-- If test.entry is defined, run a single test, otherwise run all valid tests -->
+ <!-- N.B. test.entry must be the full path to the test class, for example:
+ ant test -Dtest.entry=org.apache.commons.math.util.FastMathTestPerformance
+ -->
+ <test name="${test.entry}" todir="${test.reports}" if="test.entry"/>
+ <batchtest todir="${test.reports}" unless="test.entry">
+ <fileset dir="${test.home}">
+ <include name="**/*Test.java"/>
+ <include name="**/*TestBinary.java"/>
+ <include name="**/*TestPermutations.java"/>
+ <exclude name="**/*AbstractTest.java"/>
+ </fileset>
+ </batchtest>
+ </junit>
+ <fail message="There were test failures.">
+ <condition>
+ <and>
+ <istrue value="${test.failonerror}"/>
+ <isset property="test.failed"/>
+ </and>
+ </condition>
+ </fail>
+ </target>
+
+
+<!-- ========== Produce JavaDocs ========================================== -->
+
+ <target name="javadoc" depends="compile" description="Create component Javadoc documentation">
+ <mkdir dir="${build.home}/apidocs"/>
+ <tstamp>
+ <format property="current.year" pattern="yyyy"/>
+ </tstamp>
+ <javadoc sourcepath="${source.home}"
+ destdir="${build.home}/apidocs"
+ packagenames="org.apache.commons.*"
+ author="true"
+ private="true"
+ version="true"
+ doctitle="<h1>${component.title} ${component.version}</h1>"
+ windowtitle="${component.title} ${component.version}"
+ bottom="Copyright (c) 2003-${current.year} Apache Software Foundation"
+ classpathref="compile.classpath">
+ <link href="http://java.sun.com/j2se/1.5.0/docs/api/"/>
+ <link href="http://commons.apache.org/collections/api"/>
+ </javadoc>
+ </target>
+
+
+<!-- ========== Create Jar ================================================ -->
+
+ <target name="jar" depends="test" description="Create jar file">
+
+ <copy file="LICENSE.txt" tofile="${build.home}/classes/META-INF/LICENSE.txt"/>
+ <copy file="NOTICE.txt" tofile="${build.home}/classes/META-INF/NOTICE.txt"/>
+
+ <manifest file="${build.home}/MANIFEST.MF">
+ <attribute name="Specification-Title" value="${component.title}"/>
+ <attribute name="Specification-Version" value="${component.version}"/>
+ <attribute name="Specification-Vendor" value="Apache Software Foundation"/>
+ <attribute name="Implementation-Title" value="${component.title}"/>
+ <attribute name="Implementation-Version" value="${component.version}"/>
+ <attribute name="Implementation-Vendor" value="Apache Software Foundation"/>
+ <attribute name="Implementation-Vendor-Id" value="org.apache"/>
+ <attribute name="X-Compile-Source-JDK" value="${compile.source}"/>
+ <attribute name="X-Compile-Target-JDK" value="${compile.target}"/>
+ </manifest>
+
+ <jar jarfile="${build.home}/${final.name}.jar"
+ basedir="${build.home}/classes"
+ manifest="${build.home}/MANIFEST.MF"/>
+ </target>
+
+
+<!-- ========== Distribution Target =========================================== -->
+
+ <target name="dist" depends="clean,jar,javadoc" description="Create distribution artifacts">
+
+ <mkdir dir="${dist.home}"/>
+
+ <!-- jar(s) -->
+ <copy todir="${dist.home}">
+ <fileset dir=".">
+ <include name="RELEASE-NOTES.txt"/>
+ </fileset>
+ <fileset dir="${build.home}">
+ <include name="*.jar"/>
+ </fileset>
+ </copy>
+
+ <!-- Binary Distro -->
+ <mkdir dir="${stage.bin.dir}/${final.name}"/>
+ <copy todir="${stage.bin.dir}/${final.name}">
+ <fileset dir=".">
+ <include name="LICENSE.txt"/>
+ <include name="NOTICE.txt"/>
+ <include name="RELEASE-NOTES.txt"/>
+ </fileset>
+ <fileset dir="${build.home}">
+ <include name="*.jar"/>
+ </fileset>
+ </copy>
+ <copy todir="${stage.bin.dir}/${final.name}/apidocs">
+ <fileset dir="${build.home}/apidocs" />
+ </copy>
+
+ <!-- Source Distro -->
+ <mkdir dir="${stage.src.dir}/${final.name}-src"/>
+ <copy todir="${stage.src.dir}/${final.name}-src">
+ <fileset dir=".">
+ <include name="*.xml"/>
+ <include name="*.txt"/>
+ <include name="*.html"/>
+ </fileset>
+ </copy>
+ <copy todir="${stage.src.dir}/${final.name}-src/src">
+ <fileset dir="src" excludes="mantissa/**,experimental/**" />
+ </copy>
+ <zip zipfile="${dist.home}/${final.name}.zip" basedir="${stage.bin.dir}"/>
+ <zip zipfile="${dist.home}/${final.name}-src.zip" basedir="${stage.src.dir}"/>
+ <tar tarfile="${dist.home}/${final.name}.tar" basedir="${stage.bin.dir}" longfile="gnu"/>
+ <tar tarfile="${dist.home}/${final.name}-src.tar" basedir="${stage.src.dir}" longfile="gnu"/>
+ <gzip src="${dist.home}/${final.name}.tar" zipfile="${dist.home}/${final.name}.tar.gz"/>
+ <gzip src="${dist.home}/${final.name}-src.tar" zipfile="${dist.home}/${final.name}-src.tar.gz"/>
+
+ <!-- clean up staging directories -->
+ <delete dir="${stage.bin.dir}"/>
+ <delete dir="${stage.src.dir}"/>
+
+ </target>
+
+
+<!-- ========== Gump Target ===================================================== -->
+
+ <target name="gump" depends="clean,test,javadoc,jar" description="Gump Target - clean,test,javadoc,jar"/>
+
+
+<!-- ========== Download Dependencies =========================================== -->
+
+ <target name="download-dependencies"
+ depends="check-availability" unless="skip.download">
+ <echo message="doing download-dependencies..." />
+ <antcall target="download-junit" />
+ </target>
+
+ <target name="check-availability">
+ <echo message="doing check-availability..." />
+ <available file="${junit.jar}" property="junit.found"/>
+ </target>
+
+ <target name="download-junit" unless="junit.found">
+ <echo message="Downloading junit..."/>
+ <mkdir dir="${download.lib.dir}" />
+ <get dest="${download.lib.dir}/junit-${junit.version}.jar"
+ usetimestamp="true" ignoreerrors="true"
+ src="http://repo1.maven.org/maven2/junit/junit/${junit.version}/junit-${junit.version}.jar"/>
+ </target>
+
+</project>
+
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..9c12f6a
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!-- commons math customization of default Checkstyle behavior -->
+<module name="Checker">
+ <property name="localeLanguage" value="en"/>
+
+ <module name="TreeWalker">
+
+ <!-- Verify that EVERY source file has the appropriate license -->
+ <module name="Header">
+ <property name="headerFile" value="${checkstyle.header.file}"/>
+ </module>
+
+ <!-- Operator must be at end of wrapped line -->
+ <module name="OperatorWrap">
+ <property name="option" value="eol"/>
+ </module>
+
+ <!-- Interfaces must be types (not just constants) -->
+ <module name="InterfaceIsType"/>
+
+ <!-- Must have class / interface header comments including scm version -->
+ <module name="JavadocType">
+ <property name="versionFormat" value="\$Revision.*\$ \$Date.*\$"/>
+ </module>
+
+ <!-- Require method javadocs, allow undeclared RTE -->
+ <module name="JavadocMethod">
+ <property name="allowUndeclaredRTE" value="true"/>
+ </module>
+
+ <!-- Require field javadoc -->
+ <module name="JavadocVariable"/>
+
+ <!-- No tabs allowed! -->
+ <module name="TabCharacter"/>
+
+ <!-- No public fields -->
+ <module name="VisibilityModifier">
+ <property name="protectedAllowed" value="true"/>
+ </module>
+
+ <!-- Require hash code override when equals is -->
+ <module name="EqualsHashCode"/>
+
+ <!-- Disallow unnecessary instantiation of Boolean, String -->
+ <module name="IllegalInstantiation">
+ <property name="classes" value="java.lang.Boolean, java.lang.String"/>
+ </module>
+
+ <!-- Required for SuppressionCommentFilter below -->
+ <module name="FileContentsHolder"/>
+
+ <!-- Import should be explicit, really needed and only from pure java packages -->
+ <module name="AvoidStarImport" />
+ <module name="UnusedImports" />
+ <module name="IllegalImport" />
+
+ <!-- Utility class should not be instantiated, they must have a private constructor -->
+ <module name="HideUtilityClassConstructor" />
+
+ <!-- Switch statements should be complete and with independent cases -->
+ <module name="FallThrough" />
+ <module name="MissingSwitchDefault" />
+
+ <!-- Constant names should obey the traditional all uppercase naming convention -->
+ <module name="ConstantName" />
+
+ <!-- Method parameters and local variables should not hide fields, except in constructors and setters -->
+ <module name="HiddenField">
+ <property name="ignoreConstructorParameter" value="true" />
+ <property name="ignoreSetter" value="true" />
+ </module>
+
+ <!-- No trailing whitespace -->
+ <module name="Regexp">
+ <property name="format" value="[ \t]+$"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Trailing whitespace"/>
+ </module>
+
+ <!-- No System.out.println() statements -->
+ <module name="Regexp">
+ <!-- no sysouts -->
+ <property name="format" value="System\.out\.println"/>
+ <property name="illegalPattern" value="true"/>
+ </module>
+
+ <!-- Authors should be in pom.xml file -->
+ <module name="Regexp">
+ <property name="format" value="@author"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="developers names should be in pom file"/>
+ </module>
+
+ <!-- Use a consistent way to put modifiers -->
+ <module name="RedundantModifier" />
+ <module name="ModifierOrder" />
+
+ <!-- Use a consistent way to put declarations -->
+ <module name="DeclarationOrder" />
+
+ <!-- Don't add up parentheses when they are not required -->
+ <module name="UnnecessaryParentheses" />
+
+ <!-- Don't use too widespread catch (Exception, Throwable, RuntimeException) -->
+ <module name="IllegalCatch" />
+
+ <!-- Don't use = or != for string comparisons -->
+ <module name="StringLiteralEquality" />
+
+ <!-- Don't declare multiple variables in the same statement -->
+ <module name="MultipleVariableDeclarations" />
+
+ <!-- String literals more than one character long should not be repeated several times -->
+ <!-- the "unchecked" string is also accepted to allow @SuppressWarnings("unchecked") -->
+ <module name="MultipleStringLiterals" >
+ <property name="ignoreStringsRegexp" value='^(("")|(".")|("unchecked"))$'/>
+ </module>
+
+ <!-- <module name="TodoComment" /> -->
+
+ </module>
+
+ <!-- Require files to end with newline characters -->
+ <module name="NewlineAtEndOfFile"/>
+
+ <!-- Require package javadoc -->
+ <module name="PackageHtml"/>
+
+ <!-- Setup special comments to suppress specific checks from source files -->
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE\: stop JavadocVariable"/>
+ <property name="onCommentFormat" value="CHECKSTYLE\: resume JavadocVariable"/>
+ <property name="checkFormat" value="JavadocVariable"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE\: stop JavadocMethodCheck"/>
+ <property name="onCommentFormat" value="CHECKSTYLE\: resume JavadocMethodCheck"/>
+ <property name="checkFormat" value="JavadocMethodCheck"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE\: stop ConstantName"/>
+ <property name="onCommentFormat" value="CHECKSTYLE\: resume ConstantName"/>
+ <property name="checkFormat" value="ConstantName"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE\: stop HideUtilityClassConstructor"/>
+ <property name="onCommentFormat" value="CHECKSTYLE\: resume HideUtilityClassConstructor"/>
+ <property name="checkFormat" value="HideUtilityClassConstructor"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CHECKSTYLE\: stop MultipleVariableDeclarations"/>
+ <property name="onCommentFormat" value="CHECKSTYLE\: resume MultipleVariableDeclarations"/>
+ <property name="checkFormat" value="MultipleVariableDeclarations"/>
+ </module>
+
+</module>
+
diff --git a/findbugs-exclude-filter.xml b/findbugs-exclude-filter.xml
new file mode 100644
index 0000000..6b93ca0
--- /dev/null
+++ b/findbugs-exclude-filter.xml
@@ -0,0 +1,254 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This file contains some false positive bugs detected by findbugs. Their
+ false positive nature has been analyzed individually and they have been
+ put here to instruct findbugs it must ignore them.
+-->
+<FindBugsFilter>
+
+ <!-- the following equality tests are part of the reference algorithms -->
+ <!-- which already know about limited precision of the double numbers -->
+ <Match>
+ <Class name="org.apache.commons.math.distribution.SaddlePointExpansion" />
+ <Or>
+ <Method name="getDeviancePart" params="double,double" returns="double" />
+ <Method name="getStirlingError" params="double" returns="double" />
+ </Or>
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.univariate.BrentOptimizer" />
+ <Method name="localMin" params="boolean,double,double,double,double,double" returns="double" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.analysis.solvers.BrentSolver" />
+ <Method name="solve" params="org.apache.commons.math.analysis.UnivariateRealFunction,double,double,double,double,double,double" returns="double" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.analysis.solvers.MullerSolver" />
+ <Or>
+ <Method name="solve" params="org.apache.commons.math.analysis.UnivariateRealFunction,double,double" returns="double" />
+ <Method name="solve2" params="org.apache.commons.math.analysis.UnivariateRealFunction,double,double" returns="double" />
+ </Or>
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.EigenDecompositionImpl" />
+ <Method name="findEigenVectors" params="double[][]" returns="void" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+
+ <!-- The following equality test is intentional and needed for semantic purposes -->
+ <Match>
+ <Class name="org.apache.commons.math.geometry.Vector3D" />
+ <Method name="equals" params="java.lang.Object" returns="boolean" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.linear.LinearConstraint" />
+ <Method name="equals" params="java.lang.Object" returns="boolean" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+
+ <!-- The following equality test is intentional and needed for rounding purposes -->
+ <Match>
+ <Class name="org.apache.commons.math.util.MathUtils" />
+ <Method name="roundUnscaled" params="double,double,int" returns="double" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+
+ <!-- The following equality test is intentional for division protection -->
+ <Match>
+ <Class name="org.apache.commons.math.analysis.interpolation.LoessInterpolator" />
+ <Method name="smooth" params="double[],double[]" returns="double[]" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+
+ <!-- The following equality test is intentional for infinity detection -->
+ <Match>
+ <Class name="org.apache.commons.math.util.FastMath" />
+ <Method name="atan2" params="double,double" returns="double" />
+ <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+ </Match>
+
+ <!-- Spurious: Findbugs confused by final local variables -->
+ <Match>
+ <Class name="org.apache.commons.math.util.FastMath" />
+ <Method name="atan" params="double,double,boolean" returns="double" />
+ <Bug pattern="DLS_DEAD_LOCAL_STORE" />
+ </Match>
+
+ <!-- the following expositions of internal representation are intentional and documented -->
+ <Match>
+ <Class name="org.apache.commons.math.stat.descriptive.AbstractUnivariateStatistic"/>
+ <Method name="getDataRef" params="" returns="double[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.RealPointValuePair"/>
+ <Method name="getPointRef" params="" returns="double[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.RealPointValuePair"/>
+ <Method name="<init>" params="double[],double,boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.VectorialPointValuePair"/>
+ <Or>
+ <Method name="getPointRef" params="" returns="double[]" />
+ <Method name="getValueRef" params="" returns="double[]" />
+ </Or>
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.optimization.VectorialPointValuePair"/>
+ <Method name="<init>" params="double[],double[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.ode.sampling.DummyStepInterpolator"/>
+ <Method name="<init>" params="double[],double[],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.ode.sampling.AbstractStepInterpolator"/>
+ <Or>
+ <Method name="getInterpolatedState" params="" returns="double[]" />
+ <Method name="getInterpolatedDerivatives" params="" returns="double[]" />
+ </Or>
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.ode.sampling.NordsieckStepInterpolator"/>
+ <Method name="reinitialize" params="double,double,double[],org.apache.commons.math.linear.Array2DRowRealMatrix" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.ode.sampling.NordsieckStepInterpolator"/>
+ <Method name="getInterpolatedStateVariation" params="" returns="double[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.BigMatrixImpl"/>
+ <Method name="<init>" params="java.math.BigDecimal[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.BigMatrixImpl"/>
+ <Method name="getDataRef" params="" returns="java.math.BigDecimal[][]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.Array2DRowRealMatrix"/>
+ <Method name="<init>" params="double[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.Array2DRowRealMatrix"/>
+ <Method name="getDataRef" params="" returns="double[][]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.RealMatrixImpl"/>
+ <Method name="<init>" params="double[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.RealMatrixImpl"/>
+ <Method name="getDataRef" params="" returns="double[][]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.BlockFieldMatrix"/>
+ <Method name="<init>" params="int,int,org.apache.commons.math.FieldElement[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.Array2DRowFieldMatrix"/>
+ <Method name="<init>" params="org.apache.commons.math.FieldElement[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.Array2DRowFieldMatrix"/>
+ <Method name="getDataRef" params="" returns="org.apache.commons.math.FieldElement[][]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.BlockRealMatrix"/>
+ <Method name="<init>" params="int,int,double[][],boolean" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.util.ResizableDoubleArray"/>
+ <Or>
+ <Method name="getValues" params="" returns="double[]" />
+ <Method name="getInternalValues" params="" returns="double[]" />
+ </Or>
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.ArrayRealVector"/>
+ <Method name="getDataRef" params="" returns="double[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.linear.ArrayFieldVector"/>
+ <Method name="getDataRef" params="" returns="org.apache.commons.math.FieldElement[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.stat.clustering.EuclideanIntegerPoint"/>
+ <Method name="<init>" params="int[]" returns="void" />
+ <Bug pattern="EI_EXPOSE_REP2" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.stat.clustering.EuclideanIntegerPoint"/>
+ <Method name="getPoint" params="" returns="int[]" />
+ <Bug pattern="EI_EXPOSE_REP" />
+ </Match>
+
+ <!-- The following cases are intentional unit tests for null parameters -->
+ <Match>
+ <Class name="org.apache.commons.math.stat.StatUtilsTest" />
+ <Method name="testPercentile" params="" returns="void" />
+ <Bug pattern="NP_NULL_PARAM_DEREF_NONVIRTUAL" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.analysis.UnivariateRealSolverFactoryImplTest" />
+ <Method name="testNewNewtonSolverNull" params="" returns="void" />
+ <Bug pattern="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS" />
+ </Match>
+ <Match>
+ <Class name="org.apache.commons.math.stat.regression.OLSMultipleLinearRegressionTest" />
+ <Method name="cannotAddNullYSampleData" params="" returns="void" />
+ <Bug pattern="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS" />
+ </Match>
+
+ <!-- IntDoublePair intentionally implements Comparable inconsistently with equals -->
+ <Match>
+ <Class name="org.apache.commons.math.stat.ranking.NaturalRanking$IntDoublePair" />
+ <Bug pattern="EQ_COMPARETO_USE_OBJECT_EQUALS" />
+ </Match>
+
+</FindBugsFilter>
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 0000000..ae6f28c
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..af58bf1
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,364 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-parent</artifactId>
+ <version>18</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math</artifactId>
+ <version>2.2</version>
+ <name>Commons Math</name>
+
+ <inceptionYear>2003</inceptionYear>
+ <description>The Math project is a library of lightweight, self-contained mathematics and statistics components addressing the most common practical problems not immediately available in the Java programming language or commons-lang.</description>
+
+ <url>http://commons.apache.org/math/</url>
+
+ <issueManagement>
+ <system>jira</system>
+ <url>http://issues.apache.org/jira/browse/MATH</url>
+ </issueManagement>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/math/trunk</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/math/trunk</developerConnection>
+ <url>http://svn.apache.org/viewvc/commons/proper/math/trunk</url>
+ </scm>
+
+ <distributionManagement>
+ <site>
+ <id>people.apache.org</id>
+ <name>Commons Math</name>
+ <url>scp://people.apache.org/www/commons.apache.org/math</url>
+ </site>
+ </distributionManagement>
+
+ <developers>
+ <developer>
+ <name>Mikkel Meyer Andersen</name>
+ <id>mikl</id>
+ <email>mikl at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Bill Barker</name>
+ <id>billbarker</id>
+ <email>billbarker at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Albert Davidson Chou</name>
+ <id>achou</id>
+ <email>achou at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Mark Diggory</name>
+ <id>mdiggory</id>
+ <email>mdiggory at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Robert Burrell Donkin</name>
+ <id>rdonkin</id>
+ <email>rdonkin at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Tim O'Brien</name>
+ <id>tobrien</id>
+ <email>tobrien at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Luc Maisonobe</name>
+ <id>luc</id>
+ <email>luc at apache dot org</email>
+ </developer>
+ <developer>
+ <name>J. Pietschmann</name>
+ <id>pietsch</id>
+ <email>j3322ptm at yahoo dot de</email>
+ </developer>
+ <developer>
+ <name>Dimitri Pourbaix</name>
+ <id>dimpbx</id>
+ <email>dimpbx at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Phil Steitz</name>
+ <id>psteitz</id>
+ <email>psteitz at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Brent Worden</name>
+ <id>brentworden</id>
+ <email>brentworden at apache dot org</email>
+ </developer>
+ <developer>
+ <name>Gilles Sadowski</name>
+ <id>erans</id>
+ <email>erans at apache dot org</email>
+ </developer>
+ </developers>
+ <contributors>
+ <contributor>
+ <name>C. Scott Ananian</name>
+ </contributor>
+ <contributor>
+ <name>Mark Anderson</name>
+ </contributor>
+ <contributor>
+ <name>Rémi Arntzen</name>
+ </contributor>
+ <contributor>
+ <name>Michael Bjorkegren</name>
+ </contributor>
+ <contributor>
+ <name>John Bollinger</name>
+ </contributor>
+ <contributor>
+ <name>Cyril Briquet</name>
+ </contributor>
+ <contributor>
+ <name>Paul Cowan</name>
+ </contributor>
+ <contributor>
+ <name>Benjamin Croizet</name>
+ </contributor>
+ <contributor>
+ <name>Larry Diamond</name>
+ </contributor>
+ <contributor>
+ <name>Rodrigo di Lorenzo Lopes</name>
+ </contributor>
+ <contributor>
+ <name>Hasan Diwan</name>
+ </contributor>
+ <contributor>
+ <name>Ted Dunning</name>
+ </contributor>
+ <contributor>
+ <name>John Gant</name>
+ </contributor>
+ <contributor>
+ <name>Ken Geis</name>
+ </contributor>
+ <contributor>
+ <name>Bernhard Grünewaldt</name>
+ </contributor>
+ <contributor>
+ <name>Elliotte Rusty Harold</name>
+ </contributor>
+ <contributor>
+ <name>Matthias Hummel</name>
+ </contributor>
+ <contributor>
+ <name>Ismael Juma</name>
+ </contributor>
+ <contributor>
+ <name>Eugene Kirpichov</name>
+ </contributor>
+ <contributor>
+ <name>Piotr Kochanski</name>
+ </contributor>
+ <contributor>
+ <name>Bob MacCallum</name>
+ </contributor>
+ <contributor>
+ <name>Jake Mannix</name>
+ </contributor>
+ <contributor>
+ <name>Benjamin McCann</name>
+ </contributor>
+ <contributor>
+ <name>J. Lewis Muir</name>
+ </contributor>
+ <contributor>
+ <name>Fredrik Norin</name>
+ </contributor>
+ <contributor>
+ <name>Sujit Pal</name>
+ </contributor>
+ <contributor>
+ <name>Todd C. Parnell</name>
+ </contributor>
+ <contributor>
+ <name>Andreas Rieger</name>
+ </contributor>
+ <contributor>
+ <name>Bill Rossi</name>
+ </contributor>
+ <contributor>
+ <name>Matthew Rowles</name>
+ </contributor>
+ <contributor>
+ <name>Joni Salonen</name>
+ </contributor>
+ <contributor>
+ <name>Christopher Schuck</name>
+ </contributor>
+ <contributor>
+ <name>Christian Semrau</name>
+ </contributor>
+ <contributor>
+ <name>David Stefka</name>
+ </contributor>
+ <contributor>
+ <name>Mauro Talevi</name>
+ </contributor>
+ <contributor>
+ <name>Kim van der Linde</name>
+ </contributor>
+ <contributor>
+ <name>Jörg Weimar</name>
+ </contributor>
+ <contributor>
+ <name>Xiaogang Zhang</name>
+ </contributor>
+ </contributors>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <commons.componentid>math</commons.componentid>
+ <commons.release.version>2.2</commons.release.version>
+ <commons.rc.version>RC6</commons.rc.version>
+ <commons.binary.suffix></commons.binary.suffix>
+ <commons.jira.id>MATH</commons.jira.id>
+ <commons.jira.pid>12310485</commons.jira.pid>
+ <maven.compile.source>1.5</maven.compile.source>
+ <maven.compile.target>1.5</maven.compile.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <!-- Allow severity to be overriden by the command-line option -DminSeverity=level -->
+ <minSeverity>info</minSeverity>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ <include>**/*TestBinary.java</include>
+ <include>**/*TestPermutations.java</include>
+ </includes>
+ <excludes>
+ <exclude>**/*AbstractTest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-beta-4</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/src.xml</descriptor>
+ <descriptor>src/main/assembly/bin.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>clirr-maven-plugin</artifactId>
+ <version>2.3</version>
+ <executions>
+ <execution>
+ <goals>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <resources>
+ <resource>
+ <directory>${basedir}</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>NOTICE.txt</include>
+ <include>LICENSE.txt</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${basedir}/src/main/resources/META-INF/localization</directory>
+ <targetPath>META-INF/localization</targetPath>
+ </resource>
+ </resources>
+
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.1</version>
+ <configuration>
+ <threshold>Normal</threshold>
+ <effort>Default</effort>
+ <excludeFilterFile>${basedir}/findbugs-exclude-filter.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <xmlPath>${basedir}/src/site/xdoc/changes.xml</xmlPath>
+ <issueLinkTemplatePerSystem>
+ <default>%URL%/%ISSUE%</default>
+ </issueLinkTemplatePerSystem>
+ <template>math-release-notes.vm</template>
+ <templateDirectory>templates</templateDirectory>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <configLocation>${basedir}/checkstyle.xml</configLocation>
+ <enableRulesSummary>false</enableRulesSummary>
+ <headerFile>${basedir}/license-header.txt</headerFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>clirr-maven-plugin</artifactId>
+ <version>2.2.3</version>
+ <configuration>
+ <minSeverity>${minSeverity}</minSeverity>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
+
diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml
new file mode 100644
index 0000000..653b41b
--- /dev/null
+++ b/src/main/assembly/bin.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<assembly>
+ <id></id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+ <includeSiteDirectory>false</includeSiteDirectory>
+ <fileSets>
+ <fileSet>
+ <includes>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ <include>RELEASE-NOTES.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target/site</directory>
+ <outputDirectory>docs</outputDirectory>
+ <includes>
+ <include>apidocs/**</include>
+ <include>css/**</include>
+ <include>images/**</include>
+ <include>userguide/**</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/src/main/assembly/src.xml b/src/main/assembly/src.xml
new file mode 100644
index 0000000..8bd4b58
--- /dev/null
+++ b/src/main/assembly/src.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+ <id>src</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+ <baseDirectory>
+ ${project.artifactId}-${commons.release.version}-src
+ </baseDirectory>
+ <fileSets>
+ <fileSet>
+ <includes>
+ <include>*.txt</include>
+ <include>*.xml</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>src</directory>
+ </fileSet>
+ </fileSets>
+</assembly>
+
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.
+ */
+ at 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 × e<sub>1</sub> =
+ * e<sub>1</sub> × 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 × a.
+ * @param a element to multiply
+ * @return a new element representing this × a
+ */
+ T multiply(T a);
+
+ /** Compute this ÷ a.
+ * @param a element to add
+ * @return a new element representing this ÷ 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
+ */
+ at 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 ∫ Li<sup>2</sup> where Li (x) =
+ * ∏ (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.
+ */
+ at 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).
+ */
+ at 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 ± INFINITY i) = 1 ∓ INFINITY i
+ * cos(±INFINITY + i) = NaN + NaN i
+ * cos(±INFINITY ± 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 ± INFINITY i) = NaN + NaN i
+ * cosh(±INFINITY + i) = INFINITY ± INFINITY i
+ * cosh(±INFINITY ± 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 ± INFINITY i) = NaN + NaN i
+ * exp(INFINITY + i) = INFINITY + INFINITY i
+ * exp(-INFINITY + i) = 0 + 0i
+ * exp(±INFINITY ± 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 ± INFINITY i) = INFINITY ± (π/2)i
+ * log(INFINITY + i) = INFINITY + 0i
+ * log(-INFINITY + i) = INFINITY + πi
+ * log(INFINITY ± INFINITY i) = INFINITY ± (π/4)i
+ * log(-INFINITY ± INFINITY i) = INFINITY ± (3π/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·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 ± INFINITY i) = 1 ± INFINITY i
+ * sin(±INFINITY + i) = NaN + NaN i
+ * sin(±INFINITY ± 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 ± INFINITY i) = NaN + NaN i
+ * sinh(±INFINITY + i) = ± INFINITY + INFINITY i
+ * sinh(±INFINITY ± 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 ≥ 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 ± INFINITY i) = INFINITY + NaN i
+ * sqrt(INFINITY + i) = INFINITY + 0i
+ * sqrt(-INFINITY + i) = 0 + INFINITY i
+ * sqrt(INFINITY ± INFINITY i) = INFINITY + NaN i
+ * sqrt(-INFINITY ± INFINITY i) = NaN ± 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 ± INFINITY i) = 0 + NaN i
+ * tan(±INFINITY + i) = NaN + NaN i
+ * tan(±INFINITY ± INFINITY i) = NaN + NaN i
+ * tan(±π/2 + 0 i) = ±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 ± INFINITY i) = NaN + NaN i
+ * tanh(±INFINITY + i) = NaN + 0 i
+ * tanh(±INFINITY ± INFINITY i) = NaN + NaN i
+ * tanh(0 + (π/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πk/n) + i (sin(phi + 2π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·e<sup>i·theta</sup></code>,
+ * computed as <code>r·cos(theta) + r·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, π/4) = INFINITY + INFINITY i
+ * polar2Complex(INFINITY, 0) = INFINITY + NaN i
+ * polar2Complex(INFINITY, -π/4) = INFINITY - INFINITY i
+ * polar2Complex(INFINITY, 5π/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·e<sup>i·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 × mant × (radix)<sup>exp</sup>;</p>
+ * </pre>
+ * where sign is ±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 × 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<=x<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 <= divisor < 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 √2. */
+ private static String sqr2String;
+
+ /** High precision string representation of √2 / 2. */
+ private static String sqr2ReciprocalString;
+
+ /** High precision string representation of √3. */
+ private static String sqr3String;
+
+ /** High precision string representation of √3 / 3. */
+ private static String sqr3ReciprocalString;
+
+ /** High precision string representation of π. */
+ 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 √2. */
+ private final Dfp sqr2;
+
+ /** A two elements {@link Dfp} array with value √2 split in two pieces. */
+ private final Dfp[] sqr2Split;
+
+ /** A {@link Dfp} with value √2 / 2. */
+ private final Dfp sqr2Reciprocal;
+
+ /** A {@link Dfp} with value √3. */
+ private final Dfp sqr3;
+
+ /** A {@link Dfp} with value √3 / 3. */
+ private final Dfp sqr3Reciprocal;
+
+ /** A {@link Dfp} with value π. */
+ private final Dfp pi;
+
+ /** A two elements {@link Dfp} array with value π 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 √2.
+ * @return a {@link Dfp} with value √2
+ */
+ public Dfp getSqr2() {
+ return sqr2;
+ }
+
+ /** Get the constant √2 split in two pieces.
+ * @return a {@link Dfp} with value √2 split in two pieces
+ */
+ public Dfp[] getSqr2Split() {
+ return sqr2Split.clone();
+ }
+
+ /** Get the constant √2 / 2.
+ * @return a {@link Dfp} with value √2 / 2
+ */
+ public Dfp getSqr2Reciprocal() {
+ return sqr2Reciprocal;
+ }
+
+ /** Get the constant √3.
+ * @return a {@link Dfp} with value √3
+ */
+ public Dfp getSqr3() {
+ return sqr3;
+ }
+
+ /** Get the constant √3 / 3.
+ * @return a {@link Dfp} with value √3 / 3
+ */
+ public Dfp getSqr3Reciprocal() {
+ return sqr3Reciprocal;
+ }
+
+ /** Get the constant π.
+ * @return a {@link Dfp} with value π
+ */
+ public Dfp getPi() {
+ return pi;
+ }
+
+ /** Get the constant π split in two pieces.
+ * @return a {@link Dfp} with value π 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 π 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 π
+ */
+ 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 × 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> × 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 × mant × (radix)<sup>exp</sup>;</p>
+</pre>
+where sign is ±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 < x) = <code>p</code>.
+ *
+ * @param p the desired probability
+ * @return x, such that P(X < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 ≤ X ≤ x1).
+ * <p>
+ * The default implementation uses the identity</p>
+ * <p>
+ * P(x0 ≤ X ≤ x1) = P(X ≤ x1) - P(X ≤ 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 ≤ 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 ≤ X ≤ 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 ≤ 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 ≤ X ≤ 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 ≤ x) ≤ <code>p</code>.
+ *
+ * @param p the desired probability
+ * @return the largest x such that P(X ≤ 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 < <i>lower bound</i>) <
+ * <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 < <i>upper bound</i>) >
+ * <code>p</code>
+ */
+ @Override
+ protected int getDomainUpperBound(double p) {
+ return numberOfTrials;
+ }
+
+ /**
+ * For this distribution, X, this method returns P(X ≤ 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 ≤ x) ≤ <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 ≤ 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 < <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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 < 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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ // NOTE: chi squared is skewed to the left
+ // NOTE: therefore, P(X < μ) > .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 < μ) > .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 < 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 ≤ 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 ≤ X ≤ 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 < 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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <code>p</code>
+ */
+ @Override
+ protected double getDomainUpperBound(double p) {
+ // NOTE: exponential is skewed to the left
+ // NOTE: therefore, P(X < μ) > .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 < μ) > .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 < 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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 > 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 > 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 < 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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 < μ) > .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 < μ) > .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) $
+ */
+ at 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 ≤ 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 < <i>lower bound</i>) <
+ * <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 < <i>upper bound</i>) >
+ * <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 ≥ 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 ≤ X ≤ 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 ≤ X ≤ 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 ≤ 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 ≤ X ≤ 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 ≤ 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 ≤ 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 ≤ 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 π) */
+ 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 < <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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 < <i>lower bound</i>) <
+ * <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 < <i>upper bound</i>) >
+ * <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 ≤ 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 ≤ x) ≤ <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 ≤ 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 ≤ 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 ≤ 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 ≤ 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 ≤ 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 ≤ 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 π). */
+ 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 < <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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 > 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 > 2</code> then <code>df / (df - 2)</code> </li>
+ * <li>if <code>1 < df <= 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 < <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 < 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 < 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 ≤ 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 ≤ 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 ≤ 0 or s ≤ 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 ≤ 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 ≤ 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 ≤ 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 ≤ 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 < <i>lower bound</i>) < <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 < <i>upper bound</i>) > <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 ≥ 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
+ *
+ */
+ at 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
+ *
+ */
+ at 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
+ *
+ */
+ at 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
+ *
+ */
+ at 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
+ *
+ */
+ at 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
+ *
+ */
+ at 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>) <
+ * J<sub>n</sub> × 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>) <
+ * J<sub>n</sub> × 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érique matricielle
+ * appliquée à l'art de l'ingé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
+ *
+ */
+ at 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érique matricielle appliquée à
+ * l'art de l'ingé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
+
+ */
+ at 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
+ */
+
+ at 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 π/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 θ about the unit vector (x, y, z) is the same as the
+ * rotation build from quaternion components { cos(-θ/2),
+ * x * sin(-θ/2), y * sin(-θ/2), z * sin(-θ/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 π)
+ * @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 π + a<sub>1</sub>, π
+ * - a<sub>2</sub> and π + 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 -π/2 and π/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 π (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
+ * -π/2 or +π/2, for Euler angle singularities occur when the
+ * second angle is close to 0 or π, 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 π. 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 (α) around Z
+ * (0 is +X, π/2 is +Y, π is -X and 3π/2 is -Y)
+ * @param delta elevation (δ) above (XY) plane, from -π/2 to +π/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>∞</sub> norm for the vector.
+ * @return L<sub>∞</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 (α) of the vector, between -π and +π
+ * @see #Vector3D(double, double)
+ */
+ public double getAlpha() {
+ return FastMath.atan2(y, x);
+ }
+
+ /** Get the elevation of the vector.
+ * @return elevation (δ) of the vector, between -π/2 and +π/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>∞</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>∞</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>∞</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 × n matrix A can be written as the product of three matrices:
+ * A = U × B × V<sup>T</sup> with U an m × m orthogonal matrix,
+ * B an m × n bi-diagonal matrix (lower diagonal if m < n, upper diagonal
+ * otherwise), and V an n × 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
+ */
+ at 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) $
+ */
+ at 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 × 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 × X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A × X = B
+ * @return a vector X such that A × 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 × X = B.
+ * <p>Decomposition algorithms decompose an A matrix has a product of several specific
+ * matrices from which they can solve A × X = B in least squares sense: they find X
+ * such that ||A × 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 × 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 × 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 × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × 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 × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × 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 × X = B
+ * @return a matrix X that minimizes the two norm of A × 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 × D × V<sup>T</sup>.
+ * A, V and D are all m × 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 × 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 × 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 × X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A × X - B|| is exactly 0.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A × X - B|| is exactly 0.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × X = B for symmetric matrices A.
+ * <p>
+ * This method only find exact linear solutions, i.e. solutions for
+ * which ||A × X - B|| is exactly 0.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a matrix X that minimizes the two norm of A × 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 × X = B.
+ * <p>Decomposition algorithms decompose an A matrix has a product of several specific
+ * matrices from which they can solve A × X = B in least squares sense: they find X
+ * such that ||A × 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 × 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 × 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 × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × 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 × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × 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 × X = B
+ * @return a matrix X that minimizes the two norm of A × 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×A = L×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 × 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×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 × X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A × X = B
+ * @return a vector X such that A × 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×A = L×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 × 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×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 × X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A × X = B
+ * @return a vector X such that A × 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×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×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×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×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 × 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×n, Q is m×m and R m×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 × X = B.
+ * <p>The A matrix is implicit here. It is </p>
+ * @param b right-hand side of the equation A × X = B
+ * @return a vector X that minimizes the two norm of A × 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}
+ */
+ at 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>∞</sub> norm of the vector.
+ * <p>The L<sub>∞</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>∞</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>∞</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,
+ * Σ and V such that A = U × Σ × V<sup>T</sup>. Let A be
+ * a m × n matrix, then U is a m × p orthogonal matrix, Σ is a
+ * p × p diagonal matrix with positive or null elements, V is a p ×
+ * 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 Σ of the decomposition.
+ * <p>Σ is a diagonal matrix. The singular values are provided in
+ * non-increasing order, for compatibility with Jama.</p>
+ * @return the Σ matrix
+ */
+ RealMatrix getS();
+
+ /**
+ * Returns the diagonal elements of the matrix Σ of the decomposition.
+ * <p>The singular values are provided in non-increasing order, for
+ * compatibility with Jama.</p>
+ * @return the diagonal elements of the Σ 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 × n covariance matrix.
+ * <p>The covariance matrix is V × J × 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 × 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) × 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 × 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,
+ * Σ and V such that A = U × Σ × V<sup>T</sup>. Let A be
+ * a m × n matrix, then U is a m × p orthogonal matrix, Σ is a
+ * p × p diagonal matrix with positive or null elements, V is a p ×
+ * 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 × X = B in least square sense.
+ * <p>
+ * The m×n matrix A may not be square, the solution X is such that
+ * ||A × X - B|| is minimal.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × X = B in least square sense.
+ * <p>
+ * The m×n matrix A may not be square, the solution X is such that
+ * ||A × X - B|| is minimal.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a vector X that minimizes the two norm of A × 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 × X = B in least square sense.
+ * <p>
+ * The m×n matrix A may not be square, the solution X is such that
+ * ||A × X - B|| is minimal.
+ * </p>
+ * @param b
+ * right-hand side of the equation A × X = B
+ * @return a matrix X that minimizes the two norm of A × 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 × m matrix A can be written as the product of three matrices:
+ * A = Q × T × Q<sup>T</sup> with Q an orthogonal matrix and T a symmetrical
+ * tridiagonal matrix. Both Q and T are m × 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
+ */
+ at 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
+ */
+ at 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 × (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
+ */
+ at 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
+ */
+ at 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
+ */
+ at 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
+ */
+ at 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
+ */
+ at 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) + ∑<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)×(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) + ∑<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)×(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 ... ±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 ... ±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) + ∑<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)×(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 ... ±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
+ * ∑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 = ∑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° 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×(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 ∑(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,
+ * ω and φ such that f (t) = a cos (ω t + φ).
+ * </p>
+ *
+ * <p>From the analytical expression, we can compute two primitives :
+ * <pre>
+ * If2 (t) = ∫ f<sup>2</sup> = a<sup>2</sup> × [t + S (t)] / 2
+ * If'2 (t) = ∫ f'<sup>2</sup> = a<sup>2</sup> ω<sup>2</sup> × [t - S (t)] / 2
+ * where S (t) = sin (2 (ω t + φ)) / (2 ω)
+ * </pre>
+ * </p>
+ *
+ * <p>We can remove S between these expressions :
+ * <pre>
+ * If'2 (t) = a<sup>2</sup> ω<sup>2</sup> t - ω<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 × t + B × 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 × (t<sub>i</sub> - t<sub>1</sub>) + B × (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 × x<sub>i</sub> + B × y<sub>i</sub>, the
+ * coefficients A and B that minimize a least square criterion
+ * ∑ (z<sub>i</sub> - z (x<sub>i</sub>, y<sub>i</sub>))<sup>2</sup> are given by these expressions:</p>
+ * <pre>
+ *
+ * ∑y<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>z<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑y<sub>i</sub>z<sub>i</sub>
+ * A = ------------------------
+ * ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>y<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>y<sub>i</sub>
+ *
+ * ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>z<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>z<sub>i</sub>
+ * B = ------------------------
+ * ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>y<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>y<sub>i</sub>
+ * </pre>
+ * </p>
+ *
+ *
+ * <p>In fact, we can assume both a and ω are positive and
+ * compute them directly, knowing that A = a<sup>2</sup> ω<sup>2</sup> and that
+ * B = - ω<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> = ∫ f<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * z<sub>i</sub> = ∫ f'<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * update the sums ∑x<sub>i</sub>x<sub>i</sub>, ∑y<sub>i</sub>y<sub>i</sub>, ∑x<sub>i</sub>y<sub>i</sub>, ∑x<sub>i</sub>z<sub>i</sub> and ∑y<sub>i</sub>z<sub>i</sub>
+ * end for
+ *
+ * |--------------------------
+ * \ | ∑y<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>z<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑y<sub>i</sub>z<sub>i</sub>
+ * a = \ | ------------------------
+ * \| ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>z<sub>i</sub> - ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>z<sub>i</sub>
+ *
+ *
+ * |--------------------------
+ * \ | ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>z<sub>i</sub> - ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>z<sub>i</sub>
+ * ω = \ | ------------------------
+ * \| ∑x<sub>i</sub>x<sub>i</sub> ∑y<sub>i</sub>y<sub>i</sub> - ∑x<sub>i</sub>y<sub>i</sub> ∑x<sub>i</sub>y<sub>i</sub>
+ *
+ * </pre>
+ * </p>
+
+ * <p>Once we know ω, we can compute:
+ * <pre>
+ * fc = ω f (t) cos (ω t) - f' (t) sin (ω t)
+ * fs = ω f (t) sin (ω t) + f' (t) cos (ω t)
+ * </pre>
+ * </p>
+
+ * <p>It appears that <code>fc = a ω cos (φ)</code> and
+ * <code>fs = -a ω sin (φ)</code>, so we can use these
+ * expressions to compute φ. 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 ω. */
+ private double omega;
+
+ /** Guessed phase φ. */
+ 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 ω 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 φ 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 ω.
+ * @return guessed pulsation ω
+ */
+ public double getGuessedPulsation() {
+ return omega;
+ }
+
+ /** Get the guessed phase φ.
+ * @return guessed phase φ
+ */
+ 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 ω and
+ * the phase φ: <code>f (t) = a cos (ω t + φ)</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 ω and phase φ. */
+ 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 ω (index 1) and phase φ (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 (ω t + φ)</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 ω. */
+ private final double omega;
+
+ /** Phase φ. */
+ 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 ω.
+ * @return pulsation ω
+ */
+ public double getPulsation() {
+ return omega;
+ }
+
+ /** Get the phase φ.
+ * @return phase φ
+ */
+ 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 β parameter
+ * in {@link NonLinearConjugateGradientOptimizer}.
+ * <p>
+ * The β parameter is used to compute the successive conjugate
+ * search directions. For non-linear conjugate gradients, there are
+ * two formulas to compute β:
+ * <ul>
+ * <li>Fletcher-Reeves formula</li>
+ * <li>Polak-Ribière formula</li>
+ * </ul>
+ * On the one hand, the Fletcher-Reeves formula is guaranteed to converge
+ * if the start point is close enough of the optimum whether the
+ * Polak-Ribière formula may not converge in rare cases. On the
+ * other hand, the Polak-Ribière formula is often faster when it
+ * does converge. Polak-Ribière is often used.
+ * <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è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érique matricielle
+ * appliquée à l'art de l'ingé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érique matricielle appliquée à
+ * l'art de l'ingé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è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 β 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> <= 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> <=
+ * 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 (=, <=, >=). */
+ 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> <= 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> <= 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> <=
+ * 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> <=
+ * 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> <= 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> <=
+ * 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 <= phase 1 objective
+ * 0 1 -15 -10 0 0 0 0 0 <= phase 2 objective
+ * 0 0 1 0 0 1 0 0 2 <= constraint 1
+ * 0 0 0 1 0 0 1 0 3 <= constraint 2
+ * 0 0 1 1 0 0 0 1 4 <= constraint 3
+ * </pre>
+ * W: Phase 1 objective function</br>
+ * Z: Phase 2 objective function</br>
+ * x1 & x2: Decision variables</br>
+ * x-: Extra decision variable to allow for negative values</br>
+ * s1 & 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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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
+ * [-√3, +√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 = μ. */
+ public static final int UNIFORM_MODE = 2;
+
+ /** Exponential random deviates with mean = μ. */
+ public static final int EXPONENTIAL_MODE = 3;
+
+ /** Gaussian random deviates with mean = μ, std dev = σ. */
+ 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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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çois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+
+ * <p>This generator is described in a paper by Franç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ç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/√π <sub>0</sub>∫<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/√π <sub>x</sub>∫<sup>∞</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 Γ(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(Γ(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'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) = Σ[(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) = Σ[(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(·,·)</code> is the correlation coefficient and
+ * <code>s(·)</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 <=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 <=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 < p ≤ 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 <= 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>Σ (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>
+ * Σ(weights[i]*(values[i] - weightedMean)<sup>2</sup>)/(Σ(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>
+ * Σ(weights[i]*(values[i] - weightedMean)<sup>2</sup>)/(Σ(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>
+ * Σ(weights[i]*(values[i] - mean)<sup>2</sup>)/(Σ(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>
+ * Σ(weights[i]*(values[i] - mean)<sup>2</sup>)/(Σ(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 < values.length; i++) {<br/> <span
+ style="font-weight: bold;">stat.increment(values[i]);</span><br/>
+ 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 = ∏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 = ∏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 = Σ(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 = Σ(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>
+ * <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>
+ * <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>
+ * <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>
+ * <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>
+ * <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>
+ * <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>
+ * <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>
+ * <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>
+ * ∑[(K * observed1[i] - observed2[i]/K)<sup>2</sup> / (observed1[i] + observed2[i])]
+ * </code> where
+ * <br/><code>K = &sqrt;[&sum(observed2 / ∑(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 · 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>] +
+ * ∑<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(π 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>] +
+ * ∑<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(π 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> = √(1/2N) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * √(2/N) ∑<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(π 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> = √(1/2N) [f<sub>0</sub> + (-1)<sup>n</sup> f<sub>N</sub>] +
+ * √(2/N) ∑<sub>k=1</sub><sup>N-1</sup> f<sub>k</sub> cos(π 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) ∑<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(π 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) ∑<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(π 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> = √(1/2N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * √(2/N) ∑<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(π 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> = √(1/2N) [F<sub>0</sub> + (-1)<sup>k</sup> F<sub>N</sub>] +
+ * √(2/N) ∑<sub>n=1</sub><sup>N-1</sup> F<sub>n</sub> cos(π 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×log(N)) complexity with
+ * N=n<sub>1</sub>×n<sub>2</sub>×n<sub>3</sub>×...×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 π 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> = ∑<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(π 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> = ∑<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(π 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> = √(2/N) ∑<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(π 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> = √(2/N) ∑<sub>k=0</sub><sup>N-1</sup> f<sub>k</sub> sin(π 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) ∑<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(π 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) ∑<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(π 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> = √(2/N) ∑<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(π 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> = √(2/N) ∑<sub>n=0</sub><sup>N-1</sup> F<sub>n</sub> sin(π 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 × 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 × 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> +<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> +<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 π.
+ * @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>< 0 if !{@link #equals(double, double, double) equals(x, y, eps)} && x < y</li>
+ * <li>> 0 if !{@link #equals(double, double, double) equals(x, y, eps)} && 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 × 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π:<br/>
+ * <code>a = MathUtils.normalizeAngle(a, FastMath.PI);</code></li>
+ * <li>normalize an angle between -π and +π<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 π 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π interval for the result
+ * @return a-2kπ with integer k and center-π <= a-2kπ <= center+π
+ * @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>∞</sub> (max of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>∞</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>∞</sub> (max of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>∞</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>
diff --git a/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties b/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties
new file mode 100644
index 0000000..daac310
--- /dev/null
+++ b/src/main/resources/META-INF/localization/LocalizedFormats_fr.properties
@@ -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.
+
+ARGUMENT_OUTSIDE_DOMAIN = argument {0} hors domaine [{1} ; {2}]
+ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1 = les tableaux devraient avoir une diff\u00e9rence de taille de 1 ({0} != {1} + 1)
+ARRAY_SUMS_TO_ZERO = somme du tableau nulle
+ASSYMETRIC_EIGEN_NOT_SUPPORTED = la d\u00e9composition en valeurs/vecteurs propres de matrices
+AT_LEAST_ONE_COLUMN = une matrice doit comporter au moins une colonne
+AT_LEAST_ONE_ROW = une matrice doit comporter au moins une ligne
+BANDWIDTH_OUT_OF_INTERVAL = la bande passante devrait \u00eatre dans l''intervalle [0,1], elle est de {0}
+BINOMIAL_INVALID_PARAMETERS_ORDER = n doit \u00eatre sup\u00e9rieur ou \u00e9gal \u00e0 k pour le coefficient du bin\u00f4me (n,k), or n = {0}, k = {1}
+BINOMIAL_NEGATIVE_PARAMETER = n doit \u00eatre positif pour le coefficient du bin\u00f4me (n,k), or n = {0}
+CANNOT_CLEAR_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS = les statistiques bas\u00e9es sur des moments externes ne peuvent pas \u00eatre remises \u00e0 z\u00e9ro
+CANNOT_COMPUTE_0TH_ROOT_OF_UNITY = impossible de calculer la racine z\u00e9roi\u00e8me de l''unit\u00e9,
+CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA = impossible de calculer la densit\u00e9 beta en 0 lorsque alpha = {0,number}
+CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA = impossible de calculer la densit\u00e9 beta en 1 lorsque beta = %.3g
+CANNOT_COMPUTE_NTH_ROOT_FOR_NEGATIVE_N = impossible de calculer la racine ni\u00e8me pour n n\u00e9gatif ou nul : {0}
+CANNOT_CONVERT_OBJECT_TO_FRACTION = impossible de convertir l''objet sous forme d''un nombre rationnel : {0}
+CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS = impossible d''enlever un nombre d''\u00e9l\u00e9ments{0} n\u00e9gatif
+CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR = impossible de formater une instance de {0} comme un vecteur de dimension 3
+CANNOT_FORMAT_INSTANCE_AS_COMPLEX = impossible de formater une instance de {0} comme un nombre complexe
+CANNOT_FORMAT_INSTANCE_AS_REAL_VECTOR = impossible de formater une instance de {0} comme un vecteur r\u00e9el
+CANNOT_FORMAT_OBJECT_TO_FRACTION = impossible de formater l''objet sous forme d''un nombre rationnel
+CANNOT_INCREMENT_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS = les statistiques bas\u00e9es sur des moments externes ne peuvent pas \u00eatre incr\u00e9ment\u00e9es
+CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR = impossible de normer un vecteur de norme nulle
+CANNOT_RETRIEVE_AT_NEGATIVE_INDEX = impossible d''extraire un \u00e9l\u00e9ment \u00e0 un index n\u00e9gatif ({0})
+CANNOT_SET_AT_NEGATIVE_INDEX = impossible de mettre un \u00e9l\u00e9ment \u00e0 un index n\u00e9gatif ({0})
+CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY = impossible de substituer un \u00e9l\u00e9ment dans un tableau vide
+CANNOT_TRANSFORM_TO_DOUBLE = Exception de conversion dans une transformation : {0}
+CARDAN_ANGLES_SINGULARITY = singularit\u00e9 d''angles de Cardan
+CLASS_DOESNT_IMPLEMENT_COMPARABLE = la classe ({0}) n''implante pas l''interface Comparable
+CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT = la matrice orthogonale la plus proche a un d\u00e9terminant n\u00e9gatif {0}
+COLUMN_INDEX_OUT_OF_RANGE = l''index de colonne {0} est hors du domaine autoris\u00e9 [{1}, {2}]
+CONTINUED_FRACTION_INFINITY_DIVERGENCE = Divergence de fraction continue \u00e0 l''infini pour la valeur {0}
+CONTINUED_FRACTION_NAN_DIVERGENCE = Divergence de fraction continue \u00e0 NaN pour la valeur {0}
+CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR = crit\u00e8re de contraction ({0}) inf\u00e9rieur au facteur d''extension ({1}). Ceci induit une boucle infinie d''extensions/contractions car tout tableau de stockage fra\u00eechement \u00e9tendu respecte imm\u00e9diatement le crit\u00e8re de contraction.
+CONTRACTION_CRITERIA_SMALLER_THAN_ONE = crit\u00e8re de contraction inf\u00e9rieur \u00e0 un ({0}). Ceci induit une boucle infinie d''extensions/contractions car tout tableau de stockage de longueur \u00e9gale au nombre d''\u00e9l\u00e9ments respecte le crit\u00e8re de contraction.
+CONVERGENCE_FAILED = \u00c9chec de convergence
+CUMULATIVE_PROBABILITY_RETURNED_NAN = Fonction de probabilit\u00e9 cumulative retourn\u00e9 NaN \u00e0 l''argument de {0} p = {1}
+DIFFERENT_ROWS_LENGTHS = certaines lignes ont une longueur de {0} alors que d''autres ont une longueur de {1}
+DIGEST_NOT_INITIALIZED = mod\u00e8le empirique non initialis\u00e9
+DIMENSIONS_MISMATCH_2x2 = dimensions incoh\u00e9rentes : {0}x{1} \u00e0 la place de {2}x{3}
+DIMENSIONS_MISMATCH_SIMPLE = dimensions incoh\u00e9rentes {0} != {1}
+DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN = Discr\u00e8tes fonction de probabilit\u00e9 cumulative retourn\u00e9 NaN \u00e0 l''argument de {0}
+DISTRIBUTION_NOT_LOADED = aucune distribution n''a \u00e9t\u00e9 charg\u00e9e
+DUPLICATED_ABSCISSA = Abscisse {0} dupliqu\u00e9e aux indices {1} et {2}
+EMPTY_CLUSTER_IN_K_MEANS = groupe vide dans l''algorithme des k-moyennes
+EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY = tableau de coefficients polyn\u00f4miaux vide
+EMPTY_SELECTED_COLUMN_INDEX_ARRAY = tableau des indices de colonnes s\u00e9lectionn\u00e9es vide
+EMPTY_SELECTED_ROW_INDEX_ARRAY = tableau des indices de lignes s\u00e9lectionn\u00e9es vide
+EMPTY_STRING_FOR_IMAGINARY_CHARACTER = cha\u00eene vide pour le caract\u00e8 imaginaire
+ENDPOINTS_NOT_AN_INTERVAL = les bornes ne d\u00e9finissent pas un intervalle : [{0}, {1}]
+EQUAL_VERTICES_IN_SIMPLEX = sommets {0} et {1} \u00e9gaux dans la configuration du simplex
+EULER_ANGLES_SINGULARITY = singularit\u00e9 d''angles d''Euler
+EVALUATION_FAILED = erreur d''\u00e9valuation pour l''argument {0}
+EXPANSION_FACTOR_SMALLER_THAN_ONE = facteur d''extension inf\u00e9rieur \u00e0 un ({0})
+FACTORIAL_NEGATIVE_PARAMETER = n doit \u00eatre positif pour le calcul de n!, or n = {0}
+FAILED_BRACKETING = nombre d''it\u00e9rations = {0}, it\u00e9rations maximum = {1}, valeur initiale = {2}, borne inf\u00e9rieure = {3}, borne sup\u00e9rieure = {4}, valeur a finale = {5}, valeur b finale = {6}, f(a) = {7}, f(b) = {8}
+FAILED_FRACTION_CONVERSION = Impossible de convertir {0} en fraction apr\u00e8s {1} it\u00e9rations
+FIRST_COLUMNS_NOT_INITIALIZED_YET = les {0} premi\u00e8res colonnes ne sont pas encore initialis\u00e9es
+FIRST_ELEMENT_NOT_ZERO = le premier \u00e9l\u00e9ment n''est pas nul : {0}
+FIRST_ROWS_NOT_INITIALIZED_YET = les {0} premi\u00e8res lignes ne sont pas encore initialis\u00e9es
+FRACTION_CONVERSION_OVERFLOW = D\u00e9passement de capacit\u00e9 lors de la conversion de {0} en fraction ({1}/{2})
+FUNCTION_NOT_DIFFERENTIABLE = la fonction n''est pas diff\u00e9rentiable
+FUNCTION_NOT_POLYNOMIAL = la fonction n''est pas p\u00f4lynomiale
+GCD_OVERFLOW_32_BITS = d\u00e9passement de capacit\u00e9 : le PGCD de {0} et {1} vaut 2^31
+GCD_OVERFLOW_64_BITS = d\u00e9passement de capacit\u00e9 : le PGCD de {0} et {1} vaut 2^63
+HOLE_BETWEEN_MODELS_TIME_RANGES = trou de longueur {0} entre les domaines temporels des mod\u00e8les
+IDENTICAL_ABSCISSAS_DIVISION_BY_ZERO = les abscisses identiques x[{0}] == x[{1}] == {2} engendrent une division par z\u00e9ro
+INDEX_LARGER_THAN_MAX = l''index sp\u00e9cifi\u00e9 ({0}) d\u00e9passe l''index maximal courant ({1})
+INDEX_NOT_POSITIVE = l''indice ({0}) n''est pas positif
+INDEX_OUT_OF_RANGE = l''indice ({0}) est hors du domaine autoris\u00e9 [{1}, {2}]
+INFINITE_ARRAY_ELEMENT = le tableau contient l''\u00e9l\u00e9ment infini {0} \u00e0 l''index {1}
+INFINITE_VALUE_CONVERSION = les valeurs infinies ne peuvent \u00eatre converties
+INITIAL_CAPACITY_NOT_POSITIVE = la capacit\u00e9 initiale ({0}) n''est pas positive
+INITIAL_COLUMN_AFTER_FINAL_COLUMN = colonne initiale {0} apr\u00e8s la colonne finale {1}
+INITIAL_ROW_AFTER_FINAL_ROW = ligne initiale {0} apr\u00e8s la ligne finale {1}
+INPUT_DATA_FROM_UNSUPPORTED_DATASOURCE = les donn\u00e9es d''entr\u00e9e proviennent
+INSTANCES_NOT_COMPARABLE_TO_EXISTING_VALUES = l''instance de la classe {0} n''est pas comparable aux valeurs existantes
+INSUFFICIENT_DATA_FOR_T_STATISTIC = deux valeurs ou plus sont n\u00e9cessaires pour la statistique t, il y en a {0}
+INSUFFICIENT_DIMENSION = dimension {0} insuffisante, elle devrait \u00eatre au moins {1}
+INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE = l''\u00e9chantillon ne contient que {0} points alors qu''au moins {1} sont n\u00e9cessaires
+INSUFFICIENT_ROWS_AND_COLUMNS = donn\u00e9es insuffisantes : seulement {0} lignes et {1} colonnes.
+INTEGRATION_METHOD_NEEDS_AT_LEAST_ONE_PREVIOUS_POINT = la m\u00e9thode {0} n\u00e9cessite au moins un point pr\u00e9c\u00e9dent
+INTERNAL_ERROR = erreur interne, veuillez signaler l''erreur \u00e0 {0}
+INVALID_BRACKETING_PARAMETERS = param\u00e8tres d''encadrement invalides : borne inf\u00e9rieure = {0}, valeur initiale = {1}, borne sup\u00e9rieure = {2}
+INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS = param\u00e8tres de l''intervalle initial invalides : borne inf = {0}, valeur initiale = {1}, borne sup = {2}
+INVALID_ITERATIONS_LIMITS = limites d''it\u00e9rations invalides : min = {0}, max = {1}
+INVALID_MAX_ITERATIONS = valeur invalide pour le nombre maximal d''it\u00e9rations : {0}
+INVALID_REGRESSION_ARRAY= longueur du tableau de donn\u00e9es = {0} ne correspond pas au nombre d'observations = {1} et le nombre de variables explicatives = {2}
+INVALID_ROUNDING_METHOD = m\u00e9thode d''arondi {0} invalide, m\u00e9thodes valides : {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}), {11} ({12}), {13} ({14}), {15} ({16})
+ITERATOR_EXHAUSTED = it\u00e9ration achev\u00e9e
+LCM_OVERFLOW_32_BITS = d\u00e9passement de capacit\u00e9 : le MCM de {0} et {1} vaut 2^31
+LCM_OVERFLOW_64_BITS = d\u00e9passement de capacit\u00e9 : le MCM de {0} et {1} vaut 2^63
+LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = la liste des chromosomes d\u00e9passe maxPopulationSize
+LOESS_EXPECTS_AT_LEAST_ONE_POINT = la r\u00e9gression Loess n\u00e9cessite au moins un point
+LOWER_BOUND_NOT_BELOW_UPPER_BOUND = la borne inf\u00e9rieure ({0}) doit \u00eatre strictement plus petite que la borne sup\u00e9rieure ({1})
+LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT = la borne inf\u00e9rieure ({0}) devrait \u00eatre inf\u00e9rieure
+MAP_MODIFIED_WHILE_ITERATING = la table d''adressage a \u00e9t\u00e9 modifi\u00e9e pendant l''it\u00e9ration
+MAX_EVALUATIONS_EXCEEDED = nombre maximal d''\u00e9valuations ({0}) d\u00e9pass\u00e9
+MAX_ITERATIONS_EXCEEDED = nombre maximal d''it\u00e9rations ({0}) d\u00e9pass\u00e9
+MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION = pas minimal ({0,number,0.00E00}) atteint, l''int\u00e9gration n\u00e9cessite {1,number,0.00E00}
+MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS = Loess a besoin de tableaux d'abscisses et d'oordonn\u00e9es de m\u00eame taille, mais il y a {0} points en abscisse et {1} en ordonn\u00e9e
+NAN_ELEMENT_AT_INDEX = l''\u00e9l\u00e9ment {0} est un NaN
+NAN_VALUE_CONVERSION = les valeurs NaN ne peuvent \u00eatre converties
+NEGATIVE_BRIGHTNESS_EXPONENT = l''exposant de brillance devrait \u00eatre positif ou null, or e = {0}
+NEGATIVE_COMPLEX_MODULE = module n\u00e9gatif ({0}) pour un nombre complexe
+NEGATIVE_ELEMENT_AT_2D_INDEX = l''\u00e9l\u00e9ment ({0}, {1}) est n\u00e9gatif : {2}
+NEGATIVE_ELEMENT_AT_INDEX = l''\u00e9l\u00e9ment {0} est n\u00e9gatif : {1}
+NEGATIVE_NUMBER_OF_SUCCESSES = le nombre de succ\u00e8s ne doit pas \u00eatre n\u00e9gatif ({0})
+NEGATIVE_NUMBER_OF_TRIALS = le nombre d''essais ne doit pas \u00eatre n\u00e9gatif ({0})
+NEGATIVE_ROBUSTNESS_ITERATIONS = le nombre d''it\u00e9rations robuste ne peut \u00eatre n\u00e9gatif, alors qu''il est de {0}
+START_POSITION = position de d\u00e9part
+NON_CONVERGENT_CONTINUED_FRACTION = \u00c9chec de convergence de fraction continue pour la valeur {0}
+NON_POSITIVE_MICROSPHERE_ELEMENTS = le nombre d''\u00e9l\u00e9ments de la microsph\u00e8re devrait \u00eatre positif, or n = {0}
+NON_POSITIVE_POLYNOMIAL_DEGREE = le polyn\u00f4me doit \u00eatre de degr\u00e9 positif : degr\u00e9 = {0}
+NON_REAL_FINITE_ABSCISSA = toutes les abscisses doivent \u00eatre des nombres r\u00e9els finis, mais l''abscisse {0} vaut {1}
+NON_REAL_FINITE_ORDINATE = toutes les ordonn\u00e9es doivent \u00eatre des nombres r\u00e9els finis, mais l''ordonn\u00e9e {0} vaut {1}
+NON_REAL_FINITE_WEIGHT = tous les poids doivent \u00eatre des nombres r\u00e9els finis, mais le poids {0} vaut {1}
+NON_SQUARE_MATRIX = une matrice {0}x{1} a \u00e9t\u00e9 fournie \u00e0 la place d''une matrice carr\u00e9e
+NORMALIZE_INFINITE = impossible de normaliser vers une valeur infinie
+NORMALIZE_NAN = impossible de normaliser vers NaN
+NOT_ADDITION_COMPATIBLE_MATRICES = les dimensions {0}x{1} et {2}x{3} sont incompatibles pour l''addition matricielle
+NOT_DECREASING_NUMBER_OF_POINTS = les points {0} et {1} ne sont pas d\u00e9croissants ({2} < {3})
+NOT_DECREASING_SEQUENCE = les points {3} et {2} ne sont pas d\u00e9croissants ({1} < {0})
+NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS = pas assez de donn\u00e9es ({0} lignes) pour {1} pr\u00e9dicteurs
+NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION = une partiction spline n\u00e9cessite au moins {0} points, seuls {1} ont \u00e9t\u00e9 fournis
+NOT_INCREASING_NUMBER_OF_POINTS = les points {0} et {1} ne sont pas croissants ({2} > {3})
+NOT_INCREASING_SEQUENCE = les points {3} et {2} ne sont pas croissants ({1} > {0})
+NOT_MULTIPLICATION_COMPATIBLE_MATRICES = les dimensions {0}x{1} et {2}x{3} sont incompatibles pour la multiplication matricielle
+NOT_OVERRIDEN = m\u00e9thode n'est pas remplac\u00e9e
+NOT_POSITIVE_ALPHA = alpha doit \u00eatre positif ({0})
+NOT_POSITIVE_BETA = beta doit \u00eatre positif ({0})
+NOT_POSITIVE_COLUMNDIMENSION = nombre de colonnes invalide : {0} (doit \u00eatre positif)
+NOT_POSITIVE_DEFINITE_MATRIX = matrice non d\u00e9finie positive
+NOT_POSITIVE_DEGREES_OF_FREEDOM = les degr\u00e9s de libert\u00e9 doivent \u00eatre positifs ({0})
+NOT_POSITIVE_ELEMENT_AT_INDEX = l''\u00e9l\u00e9ment {0} n''est pas positif : {1}
+NOT_POSITIVE_EXPONENT = exposant {0} invalide (doit \u00eatre positif)
+NOT_POSITIVE_LENGTH = la longueur doit \u00eatre positive ({0})
+LENGTH = longueur ({0})
+NOT_POSITIVE_MEAN = la moyenne doit \u00eatre positive ({0})
+MEAN = moyenne ({0})
+NOT_POSITIVE_NUMBER_OF_SAMPLES = le nombre d''\u00e9chantillons n''est pas positif : {0}
+NUMBER_OF_SAMPLES = nombre d''\u00e9chantillons ({0})
+NOT_POSITIVE_PERMUTATION = la permutation k ({0}) doit \u00eatre positive
+PERMUTATION_SIZE = taille de la permutation
+NOT_POSITIVE_POISSON_MEAN = la moyenne de Poisson doit \u00eatre positive ({0})
+NOT_POSITIVE_POPULATION_SIZE = la taille de la population doit \u00eatre positive ({0})
+NOT_POSITIVE_ROW_DIMENSION = nombre de lignes invalide : {0} (doit \u00eatre positif)
+NOT_POSITIVE_SAMPLE_SIZE = la taille de l''\u00e9chantillon doit \u00eatre positive ({0})
+NOT_POSITIVE_SCALE = l''\u00e9chelle doit \u00eatre positive ({0})
+NOT_POSITIVE_SHAPE = le facteur de forme doit \u00eatre positif ({0})
+NOT_POSITIVE_STANDARD_DEVIATION = l''\u00e9cart type doit \u00eatre positif ({0})
+STANDARD_DEVIATION = \u00e9cart type
+NOT_POSITIVE_UPPER_BOUND = la borne sup\u00e9rieure doit \u00eatre positive ({0})
+NOT_POSITIVE_WINDOW_SIZE = la taille de la fen\u00eatre doit \u00eatre positive ({0})
+NOT_POWER_OF_TWO = {0} n''est pas une puissance de 2
+NOT_POWER_OF_TWO_CONSIDER_PADDING = {0} n''est pas une puissance de 2, ajoutez des \u00e9l\u00e9ments pour corriger
+NOT_POWER_OF_TWO_PLUS_ONE = {0} n''est pas une puissance de 2 plus un
+NOT_STRICTLY_DECREASING_NUMBER_OF_POINTS = les points {0} et {1} ne sont pas strictement d\u00e9croissants ({2} <= {3})
+NOT_STRICTLY_DECREASING_SEQUENCE = les points {3} et {2} ne sont pas strictement d\u00e9croissants ({1} <= {0})
+NOT_STRICTLY_INCREASING_KNOT_VALUES = les n\u0153uds d''interpolation doivent \u00eatre strictement croissants
+NOT_STRICTLY_INCREASING_NUMBER_OF_POINTS = les points {0} et {1} ne sont pas strictement croissants ({2} >= {3})
+NOT_STRICTLY_INCREASING_SEQUENCE = les points {3} et {2} ne sont pas strictement croissants ({1} >= {0})
+NOT_SUBTRACTION_COMPATIBLE_MATRICES = les dimensions {0}x{1} et {2}x{3} sont incompatibles pour la soustraction matricielle
+NOT_SYMMETRIC_MATRIX = matrice non symm\u00e9trique
+NO_BIN_SELECTED = aucun compartiment s\u00e9lectionn\u00e9
+NO_CONVERGENCE_WITH_ANY_START_POINT = aucun des {0} points de d\u00e9part n''aboutit \u00e0 une convergence
+NO_DATA = aucune donn\u00e9e
+NO_DEGREES_OF_FREEDOM = aucun degr\u00e9 de libert\u00e9 ({0} mesures, {1} param\u00e8tres)
+NO_DENSITY_FOR_THIS_DISTRIBUTION = La fonction de densit\u00e9 pour cette distribution n''a pas \u00e9t\u00e9 mis en \u0153uvre
+NO_FEASIBLE_SOLUTION = aucune solution r\u00e9alisable
+NO_OPTIMUM_COMPUTED_YET = aucun optimum n''a encore \u00e9t\u00e9 calcul\u00e9
+NO_RESULT_AVAILABLE = aucun r\u00e9sultat n''est disponible
+NO_SUCH_MATRIX_ENTRY = pas d''\u00e9l\u00e9ment ({0}, {1}) dans une matrice {2}x{3}
+NULL_NOT_ALLOWED = "null" n''est pas permis
+COVARIANCE_MATRIX = matrice de covariance
+DENOMINATOR = d\u00e9nominateur
+DENOMINATOR_FORMAT = format du d\u00e9nominateur
+FRACTION = fraction
+FUNCTION = fonction
+IMAGINARY_FORMAT = format de la partie imaginaire
+INPUT_ARRAY = tableau d''entr\u00e9e
+NUMERATOR = num\u00e9rateur
+NUMERATOR_FORMAT = format du num\u00e9rateur
+OBJECT_TRANSFORMATION = exception de conversion dans une transformation
+REAL_FORMAT = format de la partie r\u00e9elle
+WHOLE_FORMAT = format complet
+NUMBER_TOO_LARGE = {0} est plus grand que le maximum ({1})
+NUMBER_TOO_SMALL = {0} est plus petit que le minimum ({1})
+NUMBER_TOO_LARGE_BOUND_EXCLUDED = {0} n''est pas strictement plus grand que le maximum ({1})
+NUMBER_TOO_SMALL_BOUND_EXCLUDED = {0} n''est pas strictement plus petit que le minimum ({1})
+NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE = le nombre de succ\u00e8s ({0}) doit \u00eatre inf\u00e9rieur ou \u00e9gal \u00e0 la taille de la population ({1})
+NUMERATOR_OVERFLOW_AFTER_MULTIPLY = d\u00e9passement de capacit\u00e9 pour le num\u00e9rateur apr\u00e8s multiplication : {0}
+N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED = l''int\u00e9grateur de Legendre-Gauss en {0} points n''est pas disponible, le nombre de points doit \u00eatre entre {1} et {2}
+OBSERVED_COUNTS_ALL_ZERO = aucune occurrence dans le tableau des observations {0}
+OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY = les occurrences observ\u00e9es sont toutes deux nulles pour l''entr\u00e9e {0}
+OUT_OF_BOUNDS_QUANTILE_VALUE = valeur de quantile {0} hors bornes, doit \u00eatre dans l''intervalle ]0, 100]
+OUT_OF_BOUND_SIGNIFICANCE_LEVEL = niveau de signification {0} hors domaine, doit \u00eatre entre {1} et {2}
+OUT_OF_ORDER_ABSCISSA_ARRAY = les abscisses doivent \u00eatre en ordre strictement croissant, mais l''\u00e9l\u00e9ment {0} vaut {1} alors que l''\u00e9l\u00e9ment {2} vaut {3}
+OUT_OF_RANGE_ROOT_OF_UNITY_INDEX = l''indice de racine de l''unit\u00e9 {0} est hors du domaine autoris\u00e9 [{1};{2}]
+OUT_OF_RANGE_SIMPLE = {0} hors du domaine [{1}, {2}]
+OVERFLOW_IN_FRACTION = d\u00e9passement de capacit\u00e9 pour la fraction {0}/{1}, son signe ne peut \u00eatre chang\u00e9
+OVERFLOW_IN_ADDITION = d\u00e9passement de capacit\u00e9 pour l''addition : {0} + {1}
+OVERFLOW_IN_SUBTRACTION = d\u00e9passement de capacit\u00e9 pour la soustraction : {0} - {1}
+PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD = acc\u00e8s impossible \u00e0 la m\u00e9thode {0}
+PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD = l''implantation de pourcentage {0} ne dispose pas de la m\u00e9thode {1}
+PERMUTATION_EXCEEDS_N = la taille de la permutation ({0}) d\u00e9passe le domaine de la permutation ({1})
+POLYNOMIAL_INTERPOLANTS_MISMATCH_SEGMENTS = le nombre d''interpolants polyn\u00f4miaux doit correspondre au nombre de segments ({0} != {1} - 1)
+POPULATION_LIMIT_NOT_POSITIVE = la limite de population doit \u00eatre positive
+POSITION_SIZE_MISMATCH_INPUT_ARRAY = la position {0} et la taille {1} sont incompatibles avec la taille du tableau d''entr\u00e9e {2}
+POWER_NEGATIVE_PARAMETERS = impossible d''\u00e9lever une valeur enti\u00e8re
+PROPAGATION_DIRECTION_MISMATCH = directions de propagation incoh\u00e9rentes
+RANDOMKEY_MUTATION_WRONG_CLASS = RandomKeyMutation ne fonctionne qu''avec la classe RandomKeys, pas avec {0}
+ROOTS_OF_UNITY_NOT_COMPUTED_YET = les racines de l''unit\u00e9 n''ont pas encore \u00e9t\u00e9 calcul\u00e9es
+ROTATION_MATRIX_DIMENSIONS = une matrice {0}x{1} ne peut pas \u00eatre une matrice de rotation
+ROW_INDEX_OUT_OF_RANGE = l''index de ligne {0} est hors du domaine autoris\u00e9 [{1}, {2}]
+SAME_SIGN_AT_ENDPOINTS = les valeurs aux bornes de la fonction devraient avoir des signes difff\u00e9rents ; bornes : [{0}, {1}], valeurs : [{2}, {3}]
+SAMPLE_SIZE_EXCEEDS_COLLECTION_SIZE = la taille de l''\u00e9chantillon ({0}) d\u00e9passe la taille de la collection ({1})
+SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE = la taille de l''\u00e9chantillon doit \u00eatre inf\u00e9rieure
+SIMPLEX_NEED_ONE_POINT = le simplex doit contenir au moins un point
+SIMPLE_MESSAGE = {0}
+SINGULAR_MATRIX = matrice singuli\u00e8re
+SUBARRAY_ENDS_AFTER_ARRAY_END = le sous-tableau se termine apr\u00e8s la fin du tableau
+TOO_LARGE_CUTOFF_SINGULAR_VALUE = la valeur singuli\u00e8re de coupure vaut {0}, elle ne devrait pas d\u00e9passer {1}
+TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY = impossible d''enlever {0} \u00e9l\u00e9ments d''un tableau en contenant {1}
+TOO_SMALL_BANDWIDTH = la largeur de bande doit \u00eatre assez grande pour supporter au moins 2 points. Il y a {0} donn\u00e9es et la largeur de bande doit \u00eatre au moins de {1}, or elle est seulement de {2}
+TOO_SMALL_COST_RELATIVE_TOLERANCE = trop petite tol\u00e9rance relative sur le co\u00fbt ({0}), aucune r\u00e9duction de la somme des carr\u00e9s n''est possible
+TOO_SMALL_INTEGRATION_INTERVAL = intervalle d''int\u00e9gration trop petit : {0}
+TOO_SMALL_ORTHOGONALITY_TOLERANCE = trop petite tol\u00e9rance sur l''orthogonalit\u00e9 ({0}), la solution est orthogonale \u00e0 la jacobienne
+TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE = trop petite tol\u00e9rance relative sur les param\u00e8tres ({0}), aucune am\u00e9lioration de la solution approximative n''est possible
+TWO_OR_MORE_CATEGORIES_REQUIRED = deux cat\u00e9gories ou plus sont n\u00e9cessaires, il y en a {0}
+TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED = deux valeurs ou plus sont n\u00e9cessaires pour chaque cat\u00e9gorie, une cat\u00e9gorie en a {0}
+UNABLE_TO_BRACKET_OPTIMUM_IN_LINE_SEARCH = impossible d''encadrer l''optimum lors de la recherche lin\u00e9aire
+UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM = impossible de calculer les covariances : probl\u00e8me singulier
+UNABLE_TO_FIRST_GUESS_HARMONIC_COEFFICIENTS = impossible de faire une premi\u00e8re estimation des coefficients harmoniques
+UNABLE_TO_ORTHOGONOLIZE_MATRIX = impossible de rendre la matrice orthogonale en {0} it\u00e9rations
+UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN = impossible de calculer la factorisation Q.R de la matrice jacobienne {0}x{1}
+UNABLE_TO_SOLVE_SINGULAR_PROBLEM = r\u00e9solution impossible : probl\u00e8me singulier
+UNBOUNDED_SOLUTION = solution non born\u00e9e
+UNKNOWN_MODE = mode {0} inconnu, modes connus : {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) et {11} ({12})
+UNPARSEABLE_3D_VECTOR = vecteur 3D non analysable : "{0}"
+UNPARSEABLE_COMPLEX_NUMBER = nombre complexe non analysable : "{0}"
+UNPARSEABLE_FRACTION_NUMBER = nombre fractionnaire non analysable : "{0}"
+UNPARSEABLE_REAL_VECTOR = vecteur r\u00e9el non analysable : "{0}"
+UNSUPPORTED_EXPANSION_MODE = mode d''extension {0} non support\u00e9, les modes support\u00e9s sont {1} ({2}) et {3} ({4})
+UNSUPPORTED_OPERATION = op\u00e9ration non disponible
+URL_CONTAINS_NO_DATA = l''adresse {0} ne contient aucune donn\u00e9e
+VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC = {0} valeurs ont \u00e9t\u00e9 ajout\u00e9es
+VECTOR_LENGTH_MISMATCH = taille de vecteur invalide : {0} au lieu de {1} attendue
+VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT = un vecteur doit comporter au moins un \u00e9l\u00e9ment
+WEIGHT_AT_LEAST_ONE_NON_ZERO = le tableau des poids doit contenir au moins une valeur non nulle
+WRONG_BLOCK_LENGTH = forme de tableau erron\u00e9e (bloc de longueur {0} au lieu des {1} attendus
+WRONG_NUMBER_OF_POINTS = {0} sont n\u00e9cessaires, seuls {1} ont \u00e9t\u00e9 fournis
+NUMBER_OF_POINTS = nombre de points ({0})
+ZERO_DENOMINATOR = le d\u00e9nominateur doit \u00eatre diff\u00e9rent de 0
+ZERO_DENOMINATOR_IN_FRACTION = d\u00e9nominateur null dans le nombre rationnel {0}/{1}
+ZERO_FRACTION_TO_DIVIDE_BY = division par un nombre rationnel nul : {0}/{1}
+ZERO_NORM = norme nulle
+ZERO_NORM_FOR_ROTATION_AXIS = norme nulle pour un axe de rotation
+ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR = norme nulle pour un axe de d\u00e9finition de rotation
+ZERO_NOT_ALLOWED = la valeur z\u00e9ro n''est pas autoris\u00e9e ici
diff --git a/src/main/resources/templates/math-release-notes.vm b/src/main/resources/templates/math-release-notes.vm
new file mode 100644
index 0000000..5fc35bf
--- /dev/null
+++ b/src/main/resources/templates/math-release-notes.vm
@@ -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.
+
+
+ Apache ${project.name} ${version} RELEASE NOTES
+
+## Hack to improve layout: replace all pairs of spaces with a single new-line
+$release.description.replaceAll(" ", "
+")
+
+#if ($release.getActions().size() == 0)
+No changes defined in this version.
+#else
+Changes in this version include:
+
+#if ($release.getActions('add').size() !=0)
+New features:
+#foreach($actionItem in $release.getActions('add'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o#if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+
+#if ($release.getActions('fix').size() !=0)
+Fixed Bugs:
+#foreach($actionItem in $release.getActions('fix'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o#if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+
+#if ($release.getActions('update').size() !=0)
+Changes:
+#foreach($actionItem in $release.getActions('update'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o#if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+
+#if ($release.getActions('remove').size() !=0)
+Removed:
+#foreach($actionItem in $release.getActions('remove'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o#if($!issue != "") $issue. #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+## End of main loop
+#end
+
+For complete information on ${project.name}, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache ${project.name} website:
+
+${project.url}
+
+
diff --git a/src/site/resources/images/math.gif b/src/site/resources/images/math.gif
new file mode 100644
index 0000000..766ddd2
Binary files /dev/null and b/src/site/resources/images/math.gif differ
diff --git a/src/site/resources/style/project.css b/src/site/resources/style/project.css
new file mode 100644
index 0000000..c1d541c
--- /dev/null
+++ b/src/site/resources/style/project.css
@@ -0,0 +1 @@
+ at import url("http://commons.apache.org/style/commons-maven.css");
diff --git a/src/site/resources/userguide/TrajectoryDeterminationProblem.java b/src/site/resources/userguide/TrajectoryDeterminationProblem.java
new file mode 100644
index 0000000..96e585c
--- /dev/null
+++ b/src/site/resources/userguide/TrajectoryDeterminationProblem.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.commons.math.optimization.general.EstimationException;
+import org.apache.commons.math.optimization.general.EstimatedParameter;
+import org.apache.commons.math.optimization.general.EstimationProblem;
+import org.apache.commons.math.optimization.general.LevenbergMarquardtEstimator;
+import org.apache.commons.math.optimization.general.SimpleEstimationProblem;
+import org.apache.commons.math.optimization.general.WeightedMeasurement;
+
+public class TrajectoryDeterminationProblem extends SimpleEstimationProblem {
+
+ public static void main(String[] args) {
+ try {
+ TrajectoryDeterminationProblem problem =
+ new TrajectoryDeterminationProblem(0.0, 100.0, 800.0, 1.0, 0.0);
+
+ double[][] distances = {
+ { 0.0, 806.5849 }, { 20.0, 796.8148 }, { 40.0, 791.0833 }, { 60.0, 789.6712 },
+ { 80.0, 793.1334 }, { 100.0, 797.7248 }, { 120.0, 803.2785 }, { 140.0, 813.4939 },
+ { 160.0, 826.9295 }, { 180.0, 844.0640 }, { 200.0, 863.3829 }, { 220.0, 883.3143 },
+ { 240.0, 908.6867 }, { 260.0, 934.8561 }, { 280.0, 964.0730 }, { 300.0, 992.1033 },
+ { 320.0, 1023.998 }, { 340.0, 1057.439 }, { 360.0, 1091.912 }, { 380.0, 1125.968 },
+ { 400.0, 1162.789 }, { 420.0, 1201.517 }, { 440.0, 1239.176 }, { 460.0, 1279.347 } };
+ for (int i = 0; i < distances.length; ++i) {
+ problem.addDistanceMeasurement(1.0, distances[i][0], distances[i][1]);
+ };
+
+ double[][] angles = {
+ { 10.0, 1.415423 }, { 30.0, 1.352643 }, { 50.0, 1.289290 }, { 70.0, 1.225249 },
+ { 90.0, 1.161203 }, {110.0, 1.098538 }, {130.0, 1.036263 }, {150.0, 0.976052 },
+ {170.0, 0.917921 }, {190.0, 0.861830 }, {210.0, 0.808237 }, {230.0, 0.757043 },
+ {250.0, 0.708650 }, {270.0, 0.662949 }, {290.0, 0.619903 }, {310.0, 0.579160 },
+ {330.0, 0.541033 }, {350.0, 0.505590 }, {370.0, 0.471746 }, {390.0, 0.440155 },
+ {410.0, 0.410522 }, {430.0, 0.382701 }, {450.0, 0.356957 }, {470.0, 0.332400 } };
+ for (int i = 0; i < angles.length; ++i) {
+ problem.addAngularMeasurement(3.0e7, angles[i][0], angles[i][1]);
+ };
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ System.out.println("initial position: " + problem.getX0() + " " + problem.getY0());
+ System.out.println("velocity: " + problem.getVx0() + " " + problem.getVy0());
+
+ } catch (EstimationException ee) {
+ System.err.println(ee.getMessage());
+ }
+ }
+
+ public TrajectoryDeterminationProblem(double t0,
+ double x0Guess, double y0Guess,
+ double vx0Guess, double vy0Guess) {
+ this.t0 = t0;
+ x0 = new EstimatedParameter( "x0", x0Guess);
+ y0 = new EstimatedParameter( "y0", y0Guess);
+ vx0 = new EstimatedParameter("vx0", vx0Guess);
+ vy0 = new EstimatedParameter("vy0", vy0Guess);
+
+ // inform the base class about the parameters
+ addParameter(x0);
+ addParameter(y0);
+ addParameter(vx0);
+ addParameter(vy0);
+
+ }
+
+ public double getX0() {
+ return x0.getEstimate();
+ }
+
+ public double getY0() {
+ return y0.getEstimate();
+ }
+
+ public double getVx0() {
+ return vx0.getEstimate();
+ }
+
+ public double getVy0() {
+ return vy0.getEstimate();
+ }
+
+ public void addAngularMeasurement(double wi, double ti, double ai) {
+ // let the base class handle the measurement
+ addMeasurement(new AngularMeasurement(wi, ti, ai));
+ }
+
+ public void addDistanceMeasurement(double wi, double ti, double di) {
+ // let the base class handle the measurement
+ addMeasurement(new DistanceMeasurement(wi, ti, di));
+ }
+
+ public double x(double t) {
+ return x0.getEstimate() + (t - t0) * vx0.getEstimate();
+ }
+
+ public double y(double t) {
+ return y0.getEstimate() + (t - t0) * vy0.getEstimate();
+ }
+
+ private class AngularMeasurement extends WeightedMeasurement {
+
+ public AngularMeasurement(double weight, double t, double angle) {
+ super(weight, angle);
+ this.t = t;
+ }
+
+ public double getTheoreticalValue() {
+ return Math.atan2(y(t), x(t));
+ }
+
+ public double getPartial(EstimatedParameter parameter) {
+ double xt = x(t);
+ double yt = y(t);
+ double r = Math.sqrt(xt * xt + yt * yt);
+ double u = yt / (r + xt);
+ double c = 2 * u / (1 + u * u);
+ if (parameter == x0) {
+ return -c;
+ } else if (parameter == vx0) {
+ return -c * t;
+ } else if (parameter == y0) {
+ return c * xt / yt;
+ } else {
+ return c * t * xt / yt;
+ }
+ }
+
+ private final double t;
+ private static final long serialVersionUID = -5990040582592763282L;
+
+ }
+
+ private class DistanceMeasurement extends WeightedMeasurement {
+
+ public DistanceMeasurement(double weight, double t, double angle) {
+ super(weight, angle);
+ this.t = t;
+ }
+
+ public double getTheoreticalValue() {
+ double xt = x(t);
+ double yt = y(t);
+ return Math.sqrt(xt * xt + yt * yt);
+ }
+
+ public double getPartial(EstimatedParameter parameter) {
+ double xt = x(t);
+ double yt = y(t);
+ double r = Math.sqrt(xt * xt + yt * yt);
+ if (parameter == x0) {
+ return xt / r;
+ } else if (parameter == vx0) {
+ return xt * t / r;
+ } else if (parameter == y0) {
+ return yt / r;
+ } else {
+ return yt * t / r;
+ }
+ }
+
+ private final double t;
+ private static final long serialVersionUID = 3257286197740459503L;
+
+ }
+
+ private double t0;
+ private EstimatedParameter x0;
+ private EstimatedParameter y0;
+ private EstimatedParameter vx0;
+ private EstimatedParameter vy0;
+
+}
diff --git a/src/site/resources/userguide/estimation-class-diagram.png b/src/site/resources/userguide/estimation-class-diagram.png
new file mode 100644
index 0000000..fe9b678
Binary files /dev/null and b/src/site/resources/userguide/estimation-class-diagram.png differ
diff --git a/src/site/resources/userguide/estimation-sequence-diagram.png b/src/site/resources/userguide/estimation-sequence-diagram.png
new file mode 100644
index 0000000..32b5868
Binary files /dev/null and b/src/site/resources/userguide/estimation-sequence-diagram.png differ
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..1b96201
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project name="Math">
+ <bannerRight>
+ <name>Commons Math</name>
+ <src>/images/math.gif</src>
+ <href>/index.html</href>
+ </bannerRight>
+
+ <body>
+ <menu name="Math">
+ <item name="Overview" href="/index.html"/>
+ <item name="Proposal" href="/proposal.html"/>
+ <item name="Developers Guide" href="/developers.html"/>
+ <item name="Javadoc (3.0-SNAPSHOT)" href="apidocs/index.html"/>
+ <item name="Javadoc (2.2 release)"
+ href="http://commons.apache.org/math/api-2.2/index.html"/>
+ <item name="Javadoc (2.1 release)"
+ href="http://commons.apache.org/math/api-2.1/index.html"/>
+ <item name="Javadoc (2.0 release)"
+ href="http://commons.apache.org/math/api-2.0/index.html"/>
+ <item name="Javadoc (1.2 release)"
+ href="http://commons.apache.org/math/api-1.2/index.html"/>
+ <item name="Javadoc (1.1 release)"
+ href="http://commons.apache.org/math/api-1.1/index.html"/>
+ <item name="Javadoc (1.0 release)"
+ href="http://commons.apache.org/math/api-1.0/index.html"/>
+ <item name="Issue Tracking" href="/issue-tracking.html"/>
+ <item name="Source Repository (current)"
+ href="http://svn.apache.org/viewvc/commons/proper/math/trunk"/>
+ <item name="Downloads" href="http://commons.apache.org/math/download_math.cgi"/>
+ <item name="Wiki"
+ href="http://wiki.apache.org/commons/Math"/>
+ </menu>
+
+ <menu name="User Guide">
+ <item name="Contents" href="/userguide/index.html"/>
+ <item name="Overview" href="/userguide/overview.html"/>
+ <item name="Statistics" href="/userguide/stat.html"/>
+ <item name="Data Generation" href="/userguide/random.html"/>
+ <item name="Linear Algebra" href="/userguide/linear.html"/>
+ <item name="Numerical Analysis" href="/userguide/analysis.html"/>
+ <item name="Special Functions" href="/userguide/special.html"/>
+ <item name="Utilities" href="/userguide/utilities.html"/>
+ <item name="Complex Numbers" href="/userguide/complex.html"/>
+ <item name="Distributions" href="/userguide/distribution.html"/>
+ <item name="Fractions" href="/userguide/fraction.html"/>
+ <item name="Transform Methods" href="/userguide/transform.html"/>
+ <item name="3D Geometry" href="/userguide/geometry.html"/>
+ <item name="Optimization" href="/userguide/optimization.html"/>
+ <item name="Ordinary Differential Equations" href="/userguide/ode.html"/>
+ <item name="Genetic Algorithms" href="/userguide/genetics.html"/>
+ </menu>
+
+ </body>
+</project>
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
new file mode 100644
index 0000000..400db60
--- /dev/null
+++ b/src/site/xdoc/changes.xml
@@ -0,0 +1,1312 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+This file is used by the maven-changes-plugin to generate the release notes.
+Useful ways of finding items to add to this file are:
+
+1. Add items when you fix a bug or add a feature (this makes the
+release process easy :-).
+
+2. Do a JIRA search for tickets closed since the previous release.
+
+3. Use the report generated by the maven-changelog-plugin to see all
+SVN commits. TBA how to use this with SVN.
+
+To generate the release notes from this file:
+
+mvn changes:announcement-generate
+mv target/announcement/math-release-notes.vm RELEASE_NOTES.txt
+then tweak the formatting
+(e.g. copy/paste the description to replace the one-line version)
+and commit
+
+The <action> type attribute can be add,update,fix,remove.
+-->
+
+<document>
+ <properties>
+ <title>Commons Math Release Notes</title>
+ </properties>
+ <body>
+ <!-- NOTE:
+ The description below is specially formatted so as to improve the layout of the generated release notes:
+ The parsing process removes all line feeds, replacing them with a single space.
+ The Velocity template in resources/templates has been enhanced to replace pairs of adjacent spaces
+ with a new-line in the release notes. (These spaces are ignored when displaying HTML).
+ If the output is not quite correct, check for invisible trailing spaces!
+ -->
+ <release version="2.2" date="2011-03-02" description="
+This is primarily a maintenance release, but it also includes new features and enhancements.
+
+ Users of version 2.1 are encouraged to upgrade to 2.2, as this release includes some important bug fixes.
+
+ See the detailed list of changes below for full description of all bug fixes and enhancements.
+
+ This release contains some minor compatibility breaks with version 2.1 in some internal classes but none
+ of them are in APIs likely to be accessed by user code:
+ the MessagesResources_fr class has been removed (replaced by a properties file);
+ the arguments of the EventState.reinitializeBegin method have changed;
+ some protected fields which already had public accessors in AbstractStepInterpolator have been replaced.
+
+ There is a behavior change that users of the multiple regression classes should be aware of. In version
+ 2.1, there was no way to estimate models without intercept terms, and, while this was not clear from
+ the documentation, design (X) matrices needed to include initial unitary columns. In 2.2, initial
+ unitary columns are not necessary and whether or not models include intercept terms is configurable.
+ See the change log and javadoc for the classes in org.apache.commons.math.stat.regression for details.
+
+ The major new features are:
+ a new FastMath class, both faster, more accurate and with a few additional functions than StrictMath and Math;
+ a new package for floating point arbitrary precision computing, including high level functions like exponential, sine, square root ...;
+ new linear and tricubic interpolators;
+ a new Gaussian curve fitter;
+ a new erfc function;
+ characteristic support for distributions;
+ a set of new Well Equidistributed Long-period Linear (WELL) random generators.">
+ <action dev="sebb" type="fix" issue="MATH-505">
+ TestUtils is thread-hostile. Deprecate the getters and setters.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-498">
+ FastMath is not an exact replacement for StrictMath
+ (partially fixed) added scalb(double, int), scalb(float, int)
+ </action>
+ <action dev="luc" type="fix" issue="MATH-478">
+ FastMath is not an exact replacement for StrictMath
+ (partially fixed) added hypot(double, double), nextAfter(double, double)
+ and nextAfter(float,double) (beware of the strange double second argument)
+ so that they handle special values in the way as StrictMath
+ </action>
+ <action dev="luc" type="fix" issue="MATH-497">
+ FastMath is not an exact replacement for StrictMath
+ (partially fixed) added getExponent(double) and getExponent(float)
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-496">
+ FastMath is not an exact replacement for StrictMath
+ (partially fixed) Add copySign(double), copySign(float)
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-494">
+ FastMath atan2 does not agree with StrictMath for special cases
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-489">
+ FastMath acos fails when input abs value is less than about 5.7851920321187236E-300 - returns NaN
+ </action>
+ <action dev="luc" type="fix" issue="MATH-484">
+ separate discrete event detection from adaptive step size handling in ODE solvers,
+ thus improving robustness, maintainability and speed
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-486">
+ FastMath toRadian and toDegree don't handle large double numbers well
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-483">
+ FastMath does not handle all special cases correctly
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-480">
+ Fix ulp(Infinity) to return Infinity rather than NaN
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-479">
+ FastMath.signum(-0.0) does not agree with Math.signum(-0.0)
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-478">
+ FastMath is not an exact replacement for StrictMath
+ (partially fixed) Add signum(float), ulp(float)
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-471">
+ MathUtils.equals(double, double) does not work properly for floats
+ - add equivalent (float, float) methods and basic tests
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-482">
+ FastMath.max(50.0f, -50.0f) => -50.0f; should be +50.0f
+ Fixed FastMath.max(float, float) so it returns correct value.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-467">
+ Fixed an awkward statement that triggered a false positive warning.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-364" due-to="Christian Winter">
+ Added complementary error function, erfc.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-456">
+ Modified erf (and erfc) to return extreme values for x with abs(x) > 40.
+ For these arguments, the true value is indistinguishable from an extrema as a double.
+ </action>
+ <action dev="mikl" type="add" issue="MATH-385">
+ Added characteristic support to distributions, including methods to return numerical
+ estimates of the mean and variance and upper and lower bounds of support. In version 2.2,
+ methods returning distribution characteristics have been added only to the implementation
+ classes. In version 3, supporting methods have been added to the abstract base classes
+ and distribution interfaces.
+ </action>
+ <action dev="mikl" type="update" issue="MATH-384">
+ Added a constructor and addValues(double[]) methods allowing DescriptiveStatistics to
+ be initialized with values from a double[] array. Similarly enhanced
+ ResizeableDoubleArray.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-448" due-to="Patrick Meyer">
+ Added a getUniqueCount() method to Frequency to return the number of unique
+ values included in the frequency table.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-414">
+ Modified NormalDistributionImpl.cumulativeProbability to return 0 or 1,
+ respectively for values more than 40 standard deviations from the mean.
+ For these values, the actual probability is indistinguishable from 0 or 1
+ as a double. Top coding improves performance for extreme values and prevents
+ convergence exceptions.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-420">
+ Added toString() override to StatisticalSummaryValues.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-380">
+ Deprecated the whole ode.jacobians package. It is clumsy and difficult to use. It will
+ be replaced by a completely rewritten implementation in 3.0, which will be more tightly
+ bound to the top level ode package
+ </action>
+ <action dev="luc" type="fix" issue="MATH-426" due-to="Erik van Ingen">
+ Added a normalization feature to transform samples so they have zero mean and unit standard deviation
+ </action>
+ <action dev="erans" type="add" issue="MATH-440">
+ Created "MathUserException" class to convey cause of failure between
+ layers of user code separated by a layer of Commons-Math code.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-429">
+ Fixed k-means++ to add several strategies to deal with empty clusters that may appear
+ during iterations
+ </action>
+ <action dev="luc" type="update" issue="MATH-417">
+ Improved Percentile performance by using a selection algorithm instead of a
+ complete sort, and by allowing caching data array and pivots when several
+ different percentiles are desired
+ </action>
+ <action dev="luc" type="fix" issue="MATH-391">
+ Fixed an error preventing zero length vectors to be built by some constructors
+ </action>
+ <action dev="luc" type="fix" issue="MATH-421">
+ Fixed an error preventing ODE solvers to be restarted after they have been stopped by a discrete event
+ </action>
+ <action dev="luc" type="add" issue="MATH-419">
+ Added new random number generators from the Well Equidistributed Long-period Linear (WELL).
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-409">
+ Made intercept / no intercept configurable in multiple regression classes. By default, regression
+ models are estimated with an intercept term. When the "noIntercept" property is set to
+ true, regression models are estimated without intercepts.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-415">
+ Fixed lost cause in MathRuntimeException.createInternalError. Note that the message is still the default
+ message for internal errors asking to report a bug to commons-math JIRA tracker. In order to retrieve
+ the message from the root cause, one has to get the cause itself by getCause().
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-411">
+ Modified multiple regression newSample methods to ensure that by default in all cases,
+ regression models are estimated with intercept terms. Prior to the fix for this issue,
+ newXSampleData(double[][]), newSampleData(double[], double[][]) and
+ newSampleData(double[], double[][], double[][]) all required columns of "1's" to be inserted
+ into the x[][] arrays to create a model with an intercept term; while newSampleData(double[], int, int)
+ created a model including an intercept term without requiring the unitary column. All methods have
+ been changed to eliminate the need for users to add unitary columns to specify regression models.
+ Users of OLSMultipleLinearRegression or GLSMultipleLinearRegression versions 2.0 or 2.1 should either
+ verify that their code either does not use the first set of data loading methods above or set the noIntercept
+ property to true on estimated models to get the previous behavior.
+ </action>
+ <action dev="luc" type="add" issue="MATH-412" due-to="Bill Rossi">
+ Added the dfp library providing arbitrary precision floating point computation in the spirit of
+ IEEE 854-1987 (not exactly as it uses base 1000 instead of base 10). In addition to finite numbers,
+ infinities and NaNs are available (but there are no subnormals). All IEEE 854-1987 rounding modes and
+ signaling flags are supported. The available operations are +, -, *, / and the available functions
+ are sqrt, sin, cos, tan, asin, acos, atan, exp, log.
+ </action>
+ <action dev="luc" type="add" issue="MATH-375" due-to="Bill Rossi">
+ Added faster and more accurate version of traditional mathematical functions in a FastMath
+ class intended to be a drop-in replacement for java.util.Math at source-level. Some functions
+ still directly delegates to Math but this will improve with time. Some functions like exp
+ may be twice as fast (even 3 times faster on some processors). Sine, cosine or power functions
+ show typical speedups of 1.5 times faster or more.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-386">
+ Added R-squared and adjusted R-squared statistics to OLSMultipleLinearRegression.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-392" due-to="Mark Devaney">
+ Corrected the formula used for Y variance returned by calculateYVariance and associated
+ methods in multiple regression classes (AbstractMultipleLinearRegression,
+ OLSMultipleLinearRegression, GLSMultipleLinearRegression). These methods previously returned
+ estimates of the variance in the model error term. New "calulateErrorVariance" methods have
+ been added to compute what was previously returned by calculateYVariance.
+ </action>
+ <action dev="dimpbx" type="fix" issue="MATH-406">
+ Bug fixed in Levenberg-Marquardt (handling of weights).
+ </action>
+ <action dev="dimpbx" type="fix" issue="MATH-405">
+ Bug fixed in Levenberg-Marquardt (consistency of current).
+ </action>
+ <action dev="dimpbx" type="fix" issue="MATH-377">
+ Bug fixed in chi-square computation in AbstractLeastSquaresOptimizer.
+ </action>
+ <action dev="luc" type="add" issue="MATH-400" due-to="J. Lewis Muir">
+ Added support for Gaussian curve fitting.
+ </action>
+ <action dev="erans" type="fix" issue="MATH-395">
+ Fixed several bugs in "BrentOptimizer".
+ </action>
+ <action dev="erans" type="fix" issue="MATH-393">
+ Fixed inconsistency in return values in "MultiStartUnivariateRealOptimizer".
+ </action>
+ <action dev="luc" type="add" issue="MATH-388">
+ Added a feature allowing error estimation to be computed only on a subset of
+ Ordinary Differential Equations, considered as the main set, the remaining equations
+ being considered only as an extension set that should not influence the ODE integration
+ algorithm
+ </action>
+ <action dev="erans" type="fix" issue="MATH-382">
+ Fixed bug in precondition check (method "setMicrosphereElements").
+ </action>
+ <action dev="erans" type="add" issue="MATH-379">
+ Created "MultidimensionalCounter" class.
+ </action>
+ <action dev="erans" type="update" issue="MATH-361">
+ Created package "exception" to contain the new exceptions hierarchy.
+ Created package "exception.util": utilities for the exception classes
+ (e.g. managing the localization of error messages).
+ Default policy for dealing with invalid null references: raise a
+ "NullArgumentException" (subclass of "IllegalArgumentException").
+ </action>
+ <action dev="erans" type="add" issue="MATH-378" due-to="Matthew Rowles">
+ Implementation of linear interpolation.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-361">
+ Improved localization of error messages.
+ </action>
+ <action dev="erans" type="fix" issue="MATH-376">
+ Allow multiple optimizations with a default simplex.
+ </action>
+ <action dev="erans" type="add" issue="MATH-370">
+ Added new "equalsIncludingNaN" methods that have the same semantics as the old "equals" methods.
+ The semantics of the old methods will be modified (in the next major release) such that
+ NaNs are not considered equal (to be more compliant with IEEE754).
+ </action>
+ <action dev="luc" type="fix" issue="MATH-352" >
+ Added a setQRRankingThreshold method to Levenberg-Marquardt optimizer to improve robustness
+ of rank determination.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-310">
+ Added random data generation methods to RandomDataImpl for the remaining distributions in the
+ distributions package. Added a generic nextInversionDeviate method that takes a discrete
+ or continuous distribution as argument and generates a random deviate from the distribution.
+ Also added sampling methods based on the implementations in RandomDataImpl to distributions.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-362" >
+ Fixed Levenberg-Marquardt optimizer that did not use the vectorial convergence checker.
+ Now this optimizer can use either the general vectorial convergence checker or its own
+ specialized convergence settings.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-371" due-to="Kevin Childs">
+ Fixed loss of significance error in PersonsCorrelation p-value computation causing p-values
+ smaller than the machine epsilon (~1E-16) to be reported as 0.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-369" due-to="Sasun Pundev">
+ Fix NullPointerException in BisectionSolver.solve(f, min, max, initial)
+ </action>
+ <action dev="billbarker" type="fix" issue="MATH-368">
+ Fix spelling of getSparcity [sic] method of OpenMapRealVector
+ </action>
+ <action dev="billbarker" type="fix" issue="MATH-367" due-to="Albert Huang">
+ Fix problem with the default sparseIterator when a RealVector has exactly one non-zero entry
+ </action>
+ <action dev="erans" type="add" issue="MATH-366">
+ Implementation of tricubic interpolation.
+ </action>
+ <action dev="erans" type="update" issue="MATH-365">
+ Deprecated SmoothingBicubicSplineInterpolator and SmoothingBicubicSplineInterpolatorTest.
+ Added BicubicSplineInterpolator and BicubicSplineInterpolatorTest.
+ Added SmoothingPolynomialBicubicSplineInterpolator and SmoothingPolynomialBicubicSplineInterpolatorTest.
+ Added method to clear the list of observations in PolynomialFitter.
+ </action>
+ </release>
+ <release version="2.1" date="2010-04-02" description="
+This is primarily a maintenance release, but it also includes new features and enhancements.
+
+ Users of version 2.0 are encouraged to upgrade to 2.1, as this release includes some important bug fixes.
+
+ See the detailed list of changes below for full description of all bug fixes and enhancements.
+
+ This release contains some minor API compatibility breaks with version 2.0:
+ the return type of RealVector.copy() has been changed to AbstractRealVector;
+ the no-argument constructor of MatrixUtils() has been made private;
+ the mapXxxToSelf methods of OpenMapRealVector have been removed and some method return types have been changed in this class;
+ new methods have been added to the RealVector interface;
+ several fields in AdaptiveStepSizeIntegrator have been made final;
+ DummyStepInterpolator requires an additional argument for one of its constructors;
+ some protected fields have been removed from AbstractLeastSquaresOptimizer, AbstractScalarDifferentiableOptimizer and AbstractLinearOptimizer;
+ and the isOptimal(SimplexTableau) method has been removed from SimplexSolver. ">
+ <action dev="sebb" type="fix" issue="MATH-360">
+ Fix use of wrong variable in SmoothingBicubicSplineInterpolatorTest.testPreconditions()
+ </action>
+ <action dev="erans" type="update" issue="MATH-356">
+ Added method to clear the list of observations in CurveFitter.
+ </action>
+ <action dev="erans" type="add" issue="MATH-357">
+ Implementation of bicubic interpolation.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-332" due-to="Mikkel Meyer Andersen">
+ Added density functions to remaining continuous distributions (F, T, Weibull, Cauchy).
+ As of Math 2.1, all continuous distributions implement density functions. The HasDensity
+ interface has been deprecated and in version 3.0, density(double) will be added to the
+ ContinuousDistribution interface.
+ </action>
+ <action dev="sebb" type="update" issue="MATH-337">
+ Changed equals() methods to use instanceof check rather than catching ClassCastException;
+ this also allows some null checks to be omitted.
+ </action>
+ <action dev="sebb" type="update" issue="MATH-336">
+ Removed unnecessary null checks in equals methods.
+ </action>
+ <action dev="sebb" type="fix" issue="MATH-335">
+ Fraction.hashCode() implementation was not fully consistent with Fraction.equals().
+ Changed hashCode() to use fields directly to agree with equals().
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-282">
+ Resolved multiple problems leading to inaccuracy and/or failure to compute Normal,
+ ChiSquare and Poisson probabilities, Erf and Gamma functions. Made Brent solver
+ absolute accuracy configurable for all continuous distributions.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-347" >
+ Fixed too stringent interval check in Brent solver: initial guess is now
+ allowed to be at either interval end
+ </action>
+ <action dev="luc" type="add" >
+ Added a way to compute both the final state in an Initial Value Problem (IVP)
+ for Ordinary Differential Equations (ODE) and its derivatives with respect to
+ initial state and with respect to some problem parameters. This allows wrapping
+ ODE solvers into optimization or root finding algorithms, which in turn can be
+ used to solve Boundary Value Problems (BVP). There are no implementations (yet)
+ of BVP solvers in the library.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-358" >
+ Fixed an error in events handling in ODE solvers. In some rare cases, events
+ occurring close to a step start were handled without truncating the step, making
+ them appear as is they occurred close to the step end
+ </action>
+ <action dev="luc" type="fix" >
+ Fixed a problem with getInterpolatedDerivatives returning zero derivatives when
+ an ODE step handler is configured to not use interpolation. It now returns a
+ constant but non-zero value consistent with at least one point inside the step
+ </action>
+ <action dev="luc" type="fix" issue="MATH-344" >
+ Fixed wrong return values when enpoints are roots in Brent solver with
+ a user provided initial guess
+ </action>
+ <action dev="luc" type="fix" issue="MATH-343" >
+ Fixed a missing bracketing check of initial interval in Brent solver.
+ </action>
+ <action dev="dimpbx" type="fix" issue="MATH-342">
+ In SVD, the matrices passed to EigenDecomposition are now symmetric
+ by construction (rather than simply by definition). In EigenDecomposition,
+ once the tridiagonal form is obtained, the non-significant elements are
+ set to 0.
+ </action>
+ <action dev="dimpbx" type="fix" issue="MATH-333">
+ A EigenDecompositionImpl simplified makes it possible to compute
+ the SVD of a singular matrix (with the right number of elements in
+ the diagonal matrix) or a matrix with singular value(s) of multiplicity
+ greater than 1.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-323" due-to="Larry Diamond">
+ Added SemiVariance statistic.
+ </action>
+ <action dev="luc" type="add" issue="MATH-341" >
+ Added a warning in the getCoefficients method documentation for
+ PolynomialFunctionLagrangeForm. Computation may be ill-conditioned
+ so this method should be used with care.
+ </action>
+ <action dev="luc" type="add" issue="MATH-340" >
+ Fixed an error in BigFraction multiplication for large numerators that don't
+ fit in a primitive int.
+ </action>
+ <action dev="luc" type="fix" >
+ Fixed a spurious exception in EigenDecompositionImpl when a 3x3 block
+ had two identical eigenvalues.
+ </action>
+ <action dev="luc" type="add" issue="MATH-334" >
+ Added min/max getters for real vectors (not yet in the RealVector interface for
+ compatibility purposes, but in the AbstractRealVector abstract class).
+ </action>
+ <action dev="luc" type="fix" issue="MATH-338" due-to="Vincent Morand">
+ Fixed automatic step initialization in embedded Runge-Kutta integrators.
+ The relative tolerance setting was never used, only the absolute tolerance
+ was used.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-329">
+ Fixed regression in Frequency.getPct(Object) introduced in 2.0. Cumulative
+ percent was being returned for Object arguments in place of percent.
+ </action>
+ <action dev="luc" type="add" issue="MATH-321" >
+ Singular Value Decomposition now computes either the compact SVD (using only
+ positive singular values) or truncated SVD (using a user-specified maximal
+ number of singular values).
+ </action>
+ <action dev="luc" type="fix" issue="MATH-320" >
+ Fixed Singular Value Decomposition solving of singular systems.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-239" due-to="Christian Semrau">
+ Added MathUtils methods to compute gcd and lcm for long arguments.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-287" due-to="Matthew Rowles">
+ Added support for weighted univariate statistics.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-326" due-to="Jake Mannix">
+ Fixed a wrong implementation of the Linf norm in vectors.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-324" due-to="Vincent Morand">
+ Fixed a convergence discrepancy with respect to theory in Gragg-Bulirsch-Stoer
+ integrator.
+ </action>
+ <action dev="luc" type="fix" due-to="Dimitri Pourbaix">
+ Fixed a wrong dimension check in SVD solver.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-313" due-to="Jake Mannix">
+ Added composition features for real functions.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-312" due-to="Jake Mannix">
+ Added mapping and iteration methods to vectors. Provided a default implementation
+ for the numerous simple methods in the RealVectorInterface.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-322" >
+ Fixed an error in handling very close events in ODE integration.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-305" due-to="Erik van Ingen">
+ Fixed an overflow error in MathUtils.distance that was causing KMeansPlusPlusClusterer
+ to fail with a NullPointerException when component distances between points
+ exceeded Integer.MAXVALUE.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-315" due-to="Mikkel Meyer Andersen">
+ Added generationsEvolved property to GeneticAlgorithm to track the number of generations
+ evolved by the evolve() method before reaching the StoppingCondition.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-318" due-to="Dimitri Pourbaix">
+ Fixed an index computation error in eigen decomposition. Once again, kudos to Dimitri
+ for debugging this.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-308" due-to="Dimitri Pourbaix">
+ Fixed an ArrayIndexOutOfBoundsException in eigen decomposition. Kudos to Dimitri
+ for debugging this, it was really difficult.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-309" due-to="Mikkel Meyer Andersen">
+ Fixed parameter test in RandomDataImpl#nextExponential. The method now throws
+ IllegalArgumentException for mean = 0.
+ </action>
+ <action dev="brentworden" type="update" issue="MATH-311" due-to="Nipun Jawalkar">
+ Changed probability calculations for Binomial, Poisson, and Hypergeometric
+ distributions to use Catherine Loader's saddle point approximations.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-306" due-to="Joerg Huber">
+ Removed dead code from Complex#divide.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-294">
+ Fixed implementation of RandomDataImpl#nextPoisson by implementing an alternative
+ algorithm for large means.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-300" due-to="Gilles Sadowski">
+ Added support for multidimensional interpolation using the robust microsphere algorithm.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-298">
+ Fixed implementation of EmpiricalDistributionImpl#getUpperBounds to match
+ interface contract. Added getGeneratorUpperBounds method to
+ EmpiricalDistributionImpl providing previous behavior.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-296" due-to="Eugene Kirpichov">
+ Fixed wrong results on Loess interpolation, also added a way to set weights
+ for smoothing and to ignore zero weights for coefficients computation
+ </action>
+ <action dev="luc" type="fix" issue="MATH-293" due-to="Benjamin McCann">
+ Fixed a OutOfBoundException in simplex solver when some constraints are tight.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-291" due-to="Sebb">
+ Fixed misleading number formats in error messages for adaptive
+ stepsize integrators.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-287" due-to="Matthew Rowles">
+ Added support for weighted descriptive statistics.
+ </action>
+ <action dev="psteitz" type="add">
+ Added normalizeArray method to MathUtils.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-290" due-to="Benjamin McCann">
+ Fixed a NullPointerException in simplex solver when no solution is possible
+ and some constraints are negative.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-289" >
+ Removed an unused argument in a private method in simplex solver.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-288" due-to="Benjamin McCann">
+ Fixed an error induced by entries set to 0 in simplex solver.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-286" due-to="Benjamin McCann">
+ Fixed an error leading the simplex solver to compute the right solution
+ but return another one.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-283" due-to="Michael Nischt">
+ Prevent infinite loops in multi-directional direct optimization method when
+ the start point is exactly at the optimal point.
+ </action>
+ <action dev="billbarker" type="fix" issue="MATH-297" due-to="Axel Kramer">
+ Prevent possible zero divides on eigenvectors of indefinite matrices
+ </action>
+ </release>
+ <release version="2.0" date="2009-08-03" description="
+This is a major release. It combines bug fixes, new features and
+changes to existing features. Most notable among the new features are:
+decomposition algorithms in the linear algebra package (LU, QR, Cholesky,
+SVD, eigen decomposition) which are based on the popular JAMA API (but
+much faster); support for sparse matrices and vectors;
+support for any field-based matrix (Complex, Fraction ...);
+support for genetic algorithms;
+several new optimization algorithms (Dantzig's simplex for linear
+constrained problems, conjugate gradient, Brent);
+support for curve fitting with special cases for harmonic and polynomial
+functions;
+support for state derivative in ODE step handlers;
+new multistep integrators (Adams-Bashforth and Adams-Moulton) with
+variable stepsize;
+regression algorithms;
+rank transformations;
+Mersenne twister pseudo random number generator.
+This release is NOT source and binary compatible with earlier versions
+of Commons Math. Starting with version 2.0 of the library, the minimal
+version of the Java platform required to compile and use commons-math
+is Java 5. Users are encouraged to upgrade to this version, as in addition
+to new features, this release includes numerous bug fixes. Users of
+Commons Math 1.0-1.2 should recompile their code against the 2.0 jar.
+Most of the compilation errors users will encounter after the switch
+will be due to classes moved due to package reorganization. These errors
+are simply solved by adjusting the import statements in users code.">
+ <action dev="luc" type="fix" issue="MATH-281" due-to="Albert Huang">
+ Fixed an error in RealMatrix javadoc
+ </action>
+ <action dev="luc" type="add" >
+ Added an implementation of the Mersenne twister pseudo random number generator
+ from Makoto Matsumoto and Takuji Nishimura
+ </action>
+ <action dev="luc" type="update" due-to="Gilles Sadowski">
+ Changed the return type of the various interpolation algorithms to the
+ specific implementation of UnivariateRealFunction each one uses
+ </action>
+ <action dev="luc" type="fix" issue="MATH-280">
+ The behavior of the bracket method in UnivariateRealSolverUtils has been changed to return successfully
+ when a tentative bracketing interval has a root exactly at one of its end points. Previously, such intervals
+ triggered an exception.
+ </action>
+ <action dev="luc" type="add" issue="MATH-279" due-to="Michael Bjorkegren">
+ Added a check for too few rows with respect to the number of predictors in linear regression
+ </action>
+ <action dev="luc" type="add" due-to="Dimitri Pourbaix">
+ Added a getCovariance method to singular value decomposition
+ </action>
+ <action dev="luc" type="add" issue="MATH-278" due-to="Eugene Kirpichov">
+ Added robust locally weighted regression (Loess).
+ </action>
+ <action dev="luc" type="add" issue="MATH-277" due-to="Mark Anderson">
+ Added a scalar multiply to the Complex class
+ </action>
+ <action dev="luc" type="add" >
+ Added curve fitting with a general case and two specific cases (polynomial and harmonic).
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-276" due-to="Mark Anderson">
+ Optimized Complex isNaN(), isInfinite() by moving computation to constructor.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-207" due-to="David Stefka">
+ Added Genetic Algorithm implementation.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-274" >
+ Fixed detection of not positive definite matrices in Cholesky decomposition
+ </action>
+ <action dev="luc" type="fix" issue="MATH-273" due-to="Benjamin McCann">
+ Fixed a wrong check for basic variables
+ </action>
+ <action dev="luc" type="fix" issue="MATH-272" due-to="Benjamin McCann">
+ Fixed a problem when setting some variables (several variables were set
+ instead of only one)
+ </action>
+ <action dev="luc" type="add" due-to="Gilles Sadowski">
+ Added a way to limit the number of functions evaluations in optimizers
+ (the number of iterations could already be limited)
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-267" due-to="Ted Dunning">
+ Added digamma function.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-136" due-to="John Gant">
+ Added Spearman's rank correlation (SpearmansCorrelation).
+ </action>
+ <action dev="psteitz" type="add">
+ Added support for rank transformations.
+ </action>
+ <action dev="luc" type="update">
+ Completed internationalization of all error messages
+ </action>
+ <action dev="luc" type="add" issue="MATH-266" due-to="Benjamin McCann">
+ Added a clustering package with an implementation of the k-means++ algorithm
+ </action>
+ <action dev="luc" type="fix" issue="MATH-265" due-to="Benjamin McCann">
+ Added distance1, distance and distanceInf utility methods for double and
+ int arrays in MathUtils
+ </action>
+ <action dev="luc" type="fix" issue="MATH-264" due-to="Gilles Sadowski">
+ Added an utility equality method between double numbers using tolerance
+ in ulps (Units in Last Position)
+ </action>
+ <action dev="luc" type="fix" issue="MATH-263">
+ Added getNorm1, getNormInf, distance1 and distanceInf to the Vector3D class
+ </action>
+ <action dev="luc" type="add" >
+ Added support for any type of field in linear algebra (FielxMatrix, FieldVector,
+ FieldLUDecomposition)
+ </action>
+ <action dev="luc" type="add" >
+ The RealMatrixImpl implementation classes has been renamed Array2DRowRealMatrix
+ to reflect its specificities and for consistency with the new implementations. The
+ previous name is still available but is deprecated
+ </action>
+ <action dev="luc" type="add" >
+ Added a block-based storage type for dense matrices improving speed for large dimensions
+ </action>
+ <action dev="psteitz" type="add" due-to="John Bollinger">
+ Added AggregateSummaryStatistics class to support aggregation of SummaryStatistics.
+ </action>
+ <action dev="luc" type="add" >
+ Added general Field and FieldElement interfaces to allow generic algorithms
+ to operate on fields. The library already provides several implementations:
+ Complex, Fraction, BigFraction and BigReal
+ </action>
+ <action dev="luc" type="fix" issue="MATH-257" due-to="Sebb">
+ Fixed inconsistent access to multidimensional array in FastFourierTransformer
+ </action>
+ <action dev="luc" type="fix" issue="MATH-248" >
+ Greatly improved multiplication speed for sparse matrices
+ </action>
+ <action dev="luc" type="fix" issue="MATH-253" due-to="Sebb">
+ Fixed threading issues with MathException and MathRuntimeException
+ </action>
+ <action dev="luc" type="fix" issue="MATH-254" due-to="Sebb">
+ Fixed threading issues with UnivariateRealSolverUtils factory
+ </action>
+ <action dev="luc" type="fix" issue="MATH-255" due-to="Sebb">
+ Reduced visibility of MessagesResources_fr.contents field to private
+ </action>
+ <action dev="luc" type="add" issue="MATH-256" >
+ Added Fraction.toString()
+ </action>
+ <action dev="luc" type="add">
+ Added add/subtract/multiply/divide functions with integer parameters to Fraction
+ </action>
+ <action dev="luc" type="add">
+ Added some utility functions to compute powers with integral types (int, long, BigInteger).
+ </action>
+ <action dev="luc" type="fix" issue="MATH-252">
+ Fixed a comparison error when two different fractions evaluate to the
+ same double due to limited precision.
+ </action>
+ <action dev="luc" type="add" issue="MATH-251" due-to="Benjamin Croizet">
+ Added a BigFraction class that does not overflow when big numerators or
+ denominators are used.
+ </action>
+ <action dev="luc" type="add" issue="MATH-246" due-to="Benjamin McCann">
+ Added an optimizer for constrained linear problems based on 2-phases standard simplex.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-177" >
+ Redesigned the optimization framework for a simpler yet more powerful API.
+ Added non-linear conjugate gradient optimizer.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-243" due-to="Christian Semrau">
+ Fixed an error in computing gcd and lcm for some extreme values at integer
+ range boundaries.
+ </action>
+ <action dev="luc" type="add" issue="MATH-247" due-to="Benjamin McCann">
+ Added a MathUtils method to check equality given some error bounds.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-114" due-to="John Gant">
+ Added PearsonsCorrelation class to compute correlation matrices, standard
+ errors and p-values for correlation coefficients.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-114">
+ Added Covariance class to compute variance-covariance matrices in new
+ correlation package.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-216" due-to="Cyril Briquet">
+ Improved fast Fourier transform efficiency.
+ </action>
+ <action dev="billbarker" type="add">
+ Added a SparseRealVector class that implements a sparse vector for the RealVector interface.
+ </action>
+ <action dev="luc" type="add" >
+ Added factory methods to create Chebyshev, Hermite, Laguerre and Legendre polynomials.
+ </action>
+ <action dev="luc" type="add" >
+ Added add, subtract, negate, multiply and toString methods to PolynomialFunction.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-189">
+ Changed FractionFormat to extend NumberFormat.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-242" due-to="Christian Semrau">
+ Forced symmetry in binomialCoefficientLog and added test cases for MathUtils.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-241" due-to="Christian Semrau">
+ Fixed error in binomial coefficient computation.
+ </action>
+ <action dev="luc" type="add" >
+ Added a Legendre-Gauss integrator.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-240" due-to="Christian Semrau">
+ Fixed error in factorial computation for 17 <= n <= 20.
+ </action>
+ <action dev="luc" type="update" >
+ Integration algorithms now can have both relative and absolute
+ accuracy settings.
+ </action>
+ <action dev="luc" type="add" issue="MATH-177" due-to="Gilles Sadowski">
+ Added a new univariate sub-package below the optimization package.
+ </action>
+ <action dev="luc" type="update" >
+ The analysis package has been reorganized with several sub-packages.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-238" due-to="Christian Semrau">
+ Fixed an error in gcd computation for large values.
+ </action>
+ <action dev="luc" type="add" >
+ Added method to walk matrix entries with or without changing them in the
+ visitor design pattern sense. Three different orders can be used, row by row,
+ column by column or optimized order according to internal storage. Optimized
+ order should be preferred when no specific order is needed, because it will be
+ more cache efficient.
+ </action>
+ <action dev="luc" type="add" issue="MATH-215" due-to="Bernhard Grünewaldt">
+ Added Fast Hadamard Transform.
+ </action>
+ <action dev="luc" type="add" issue="MATH-236" due-to="Bernhard Grünewaldt">
+ Added nth root computation for complex numbers.
+ </action>
+ <action dev="luc" type="add" issue="MATH-230" due-to="Sujit Pal and Ismael Juma">
+ Added support for sparse matrix.
+ </action>
+ <action dev="luc" type="add" due-to="Ismael Juma">
+ Added an int/double hash map (OpenIntToDoubleHashMap) with much smaller
+ memory overhead than standard java.util.Map (open addressing and no boxing).
+ </action>
+ <action dev="luc" type="add" issue="MATH-152" due-to="Remi Arntzen">
+ Added support for multi-dimensional Fourier transform.
+ </action>
+ <action dev="luc" type="update" issue="MATH-218" >
+ The root solvers and the integrators now take the function to solve as a
+ parameter to the solve/integrate methods, thus allowing to reuse the same
+ solver/integrator for different functions.
+ </action>
+ <action dev="luc" type="add" issue="MATH-234" >
+ Added setter methods for rows and columns in matrices.
+ </action>
+ <action dev="luc" type="add" issue="MATH-232" >
+ Added Frobenius matrix norm.
+ </action>
+ <action dev="luc" type="add" issue="MATH-231" >
+ Added an abstract matrix implementation simplifying extension by users.
+ </action>
+ <action dev="luc" type="add" issue="MATH-178" due-to="Paul Cowan">
+ Added support for the Zipf distribution.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-212" due-to="Jason C. HandUber">
+ Added support for copying statistics. Changes to stats classes
+ include copy constructor, static copy(-,-) and instance copy()
+ methods. Added copy() to UnivariateStatistic and StorelessUnivariateStatistic
+ interfaces.
+ </action>
+ <action dev="luc" type="add" issue="MATH-229" due-to="Cyril Briquet">
+ Added a removal feature for observations in descriptive statistics.
+ </action>
+ <action dev="luc" type="add" >
+ Added a scalb method in MathUtils. This method is similar to the method
+ with same name added in java.lang.Math as of Java 6.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-227" due-to="Joerg Henning">
+ Fixed F distribution inverse CDF computation for small denominator degrees of freedom.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-226" due-to="Stuart Siegel">
+ Fixed an error in CorrelatedRandomVectorGenerator leading to a component of
+ the generated vector being constant.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-223" due-to="John Mulcahy">
+ Greatly improved QR-decomposition speed using transposed matrices internally.
+ </action>
+ <action dev="luc" type="fix" due-to="Pascal Parraud">
+ Fixed an infinite loop encountered in some backward integration cases for ODE solvers.
+ </action>
+ <action dev="luc" type="add" issue="MATH-222" due-to="Ted Dunning">
+ Added beta distribution.
+ </action>
+ <action dev="luc" type="add" issue="MATH-222" due-to="Ted Dunning">
+ Added probability density functions computation for distributions for which
+ it is feasible.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-221" due-to="Dieter Roth">
+ Changed the Complex.equals() method so that it considers +0 and -0 are equal,
+ as required by IEEE-754 standard.
+ </action>
+ <action dev="luc" type="add" issue="MATH-220">
+ Added JAMA-like interfaces for eigen/singular problems. The implementation
+ are based on the very quick dqd/dqds algorithms and some parts of the MRRR
+ algorithm. This leads to very fast and accurate solutions.
+ </action>
+ <action dev="luc" type="add" issue="MATH-220" >
+ Added JAMA-like interfaces for decomposition algorithms. These interfaces
+ decompose a matrix as a product of several other matrices with predefined
+ properties and shapes depending on the algorithm. These algorithms allow to
+ solve the equation A * X = B, either for an exact linear solution
+ (LU-decomposition, Cholesky decomposition) or an exact or least-squares
+ solution (QR-decomposition).
+ </action>
+ <action dev="luc" type="add" issue="MATH-219" due-to="Andrew Berry">
+ Added removeData methods for the SimpleRegression class. This allows
+ to support regression calculations across a sliding window of (time-based)
+ observations without having to recalculate for the entire window every time.
+ </action>
+ <action dev="luc" type="add" due-to="Andreas Rieger">
+ Support for one dimensional vectors has been added to the linear algebra
+ package with a RealVector interface, a RealVectorImpl default implementation
+ using a single double array to store elements and a RealVectorFormat for
+ input/output.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-217">
+ Changed OLS regression implementation added in MATH-203 to use
+ QR decomposition to solve the normal equations.
+ </action>
+ <action dev="luc" type="add">
+ New ODE integrators have been added: the explicit Adams-Bashforth and implicit
+ Adams-Moulton multistep methods. As the implementations of these methods are based
+ on Nordsieck vector rather than a traditional array of previous steps, they both
+ have been improved to handle adaptive stepsize. These methods provide the same rich
+ features has the existing ones: continuous output, step handlers, discrete events,
+ G-stop ...
+ </action>
+ <action dev="luc" type="fix" issue="MATH-214" >
+ Replaced size adjustment of all steps of fixed steps Runge-Kutta integrators by
+ a truncation of the last step only.
+ </action>
+ <action dev="luc" type="update">
+ The ODE integrators now support several step handlers at once, instead of just one.
+ This is more consistent with event handlers management.
+ The setStepHandler method has therefore been replaced by addStephandler, the
+ getStepHandler method has been replaced by getStepHandlers which returns a Collection
+ and a clearStepHandlers method has been added.
+ </action>
+ <action dev="luc" type="add">
+ All ODE integrators now support setting a maximal number of evaluations of differential
+ equations function. If this number is exceeded, an exception will be thrown during
+ integration. This can be used to prevent infinite loops if for example error control or
+ discrete events create a really large number of extremely small steps.
+ </action>
+ <action dev="luc" type="add">
+ All step interpolators for ODE integrators now provide interpolation for
+ both the state and its time derivatives. The interpolated derivatives are
+ the exact derivatives of the interpolated state, thus preserving consistency.
+ The general step handlers hence do not need to call the derivation function
+ anymore. The fixed step handlers also get the time derivative of the state
+ as an additional argument along with the state when they are called.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-213" >
+ Changed return type for FirstOrderIntegrator.integrate() to double
+ in order to retrieve exact stop time. This allows to handle properly
+ integration interruption due to an EventHandler instance asking to
+ stop the integration when its associated event is triggered. The state
+ was already set to the current state at interruption time, but it was
+ difficult to get the corresponding time (it involved setting a step
+ handler monitoring the last step specially).
+ </action>
+ <action dev="luc" type="update" >
+ Events handlers in the ODE package now also provide the switching function
+ variation (increasing/decreasing) when an event occurs
+ </action>
+ <action dev="luc" type="update">
+ Clarified the ODE package by breaking in into several sub-packages and renaming
+ classes (SwitchingFunctions/EventHandler, SwitchingFunctionsHandler/CombinedEventsManager)
+ </action>
+ <action dev="luc" type="fix" issue="MATH-210" >
+ Changed return type for FirstOrderIntegrator.getSwitchingFunctions()
+ to a collection of SwitchingFunction instances. This better suits the
+ intended use of the method and fixes a visibility error since the
+ previous return type referenced the package private SwitchState class.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-209" due-to="Thomas Chust">
+ Fixed dimension error on output vector for the operate method
+ in RealMatrixImpl and BigMatrixImpl classes.
+ </action>
+ <action dev="luc" type="update">
+ The FirstOrderDifferentialEquations, FirstOrderIntegrator and FixedStepHandler
+ interfaces now extends Serializable, allowing integrators, problems and
+ handlers to be embedded into users Serializable classes.
+ </action>
+ <action dev="luc" type="add">
+ Added several convenience methods and constants for Vector3D and Rotation.
+ </action>
+ <action dev="luc" type="update">
+ Replaced public no argument constructors with IDENTITY or ZERO
+ static instances for immutable classes Vector3D and Rotation.
+ </action>
+ <action dev="luc" type="fix">
+ Fixed inconsistencies in the naming scheme for static fields in
+ Vector3D and Rotation with respect to the overall library.
+ </action>
+ <action dev="luc" type="update" >
+ Greatly improved RealMatrixImpl and BigMatrixImpl performances,
+ both in terms of speed and in terms of temporary memory footprint.
+ </action>
+ <action dev="luc" type="add" issue="MATH-203" due-to="Mauro Talevi">
+ Added Mauro's patch to support multiple regression.
+ </action>
+ <action dev="luc" type="update" >
+ Starting with version 2.0 of the library, the minimal version of the Java
+ platform required to compile and use commons-math is Java 5. This version
+ is widely deployed now on many systems. It brings new important features
+ for specific mathematical developments, for example new functions (log10,
+ cbrt, ulp, signum, cosh, sinh, tanh, hypot, expm1, log1p), autoboxing,
+ MathContext or RoundingMode. It also brings important features for general
+ development, for example enums, generics or annotations.
+ </action>
+ <action dev="luc" type="add" >
+ Switching functions can now throw dedicated SwitchException from all their
+ method. At upper call level, the various ODE integrators handle these new
+ exceptions and wrap them into IntegratorException instances, hence the
+ integrators methods signature did not change.
+ </action>
+ <action dev="luc" type="add" issue="MATH-202">
+ Added the getSwitchingFunctions and clearSwitchingFunctions to the
+ FirstOrderIntegrator interface and all its implementations
+ </action>
+ <action dev="luc" type="update" >
+ Removed deprecated features. This includes the following changes. Factory-based
+ instantiation replaced by setter injection in 1.2 in several classes have been
+ removed. Protected fields in matrices implementations have been declared final
+ and private. Identity factory methods moved to MatrixUtils class have been
+ removed. Complex utilities methods that have been superseded by Complex instance
+ methods have been removed.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-205" due-to="Roman Werpachowski">
+ Fixed formula in fast cosine transformer javadoc comments.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-193" due-to="Michael Heuer and Sebb">
+ Javadoc and style fixes.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-198" due-to="Frederick Salardi">
+ Added an error detection for missing imaginary character while parsing complex string
+ </action>
+ <action dev="luc" type="fix" issue="MATH-199" due-to="Mick">
+ Detect numerical problems in Q.R decomposition for Levenberg-Marquardt estimator
+ and report them appropriately
+ </action>
+ <action dev="luc" type="fix" issue="MATH-200" due-to="Plamen Petrov">
+ Fixed several crashes in getCovariances() and guessParametersErrors() in
+ AbstractEstimator when some parameters are bound. The methods now explicitly
+ give result only about unbound parameters.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-201" due-to="Peter Wyngaard">
+ Fixed truncation error in t-test classes for large t values.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-204" due-to="Mick">
+ Added root checks for the endpoints.
+ </action>
+ </release>
+ <release version="1.2" date="2008-02-24"
+ description="This release combines bug fixes and new features. Most notable
+ among the new features are the estimation, optimization, geometry and ode
+ packages added from the Mantissa library. Implementations of fast Fourier
+ transform, QR decomposition and several numerical integration algorithms
+ have also been added, along with enhancements and extensions to packages
+ included in Commons Math 1.1. This release is source and binary compatible
+ with earlier versions of Commons Math.">
+ <action dev="luc" type="fix">
+ Fixed numerous warnings in test code.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-156" due-to="Tyler Ward">
+ Use the initial guess provided by the user in BrentSolver.solve(), thus
+ improving speed.
+ </action>
+ <action dev="luc" type="add">
+ Added the estimation, optimization, geometry and ode packages from the
+ Mantissa library.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-18" due-to="Phil Steitz">
+ Made ComplexFormat format double values with a provided NumberFormat
+ instance instead of using the real part format for all values.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-120" due-to="Todd C. Parnell">
+ Added Pascal distribution implementation.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-148" due-to="Joni Salonen">
+ Added QR Decomposition.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-60" due-to="Nhung Nnguyen">
+ Modified ProperFractionFormat to reject embedded minus signs.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-151" due-to="Luc Maisonobe">
+ Added a nextAfter method in MathUtils to return the next
+ machine-representable number in a specified direction from a given
+ floating point number. Used this to ensure that MathUtils.round does
+ not return incorrect results for numbers with bad IEEE754
+ representations.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-140" due-to="Xiaogang Zhang">
+ Added Fast Fourier Transform implementation.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-85" due-to="Mark Osborn, Luc Maisonobe">
+ Modified getSumSquaredErrors method in SimpleRegression to always
+ return a non-negative result.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-153" due-to="Remi Arntzen">
+ Corrected nextInt and nextLong to handle wide value ranges.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-166" due-to="Lukas Theussl">
+ Increased default precision of Gamma and Beta functions.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-158" due-to="Hasan Diwan">
+ Added log function to MathUtils.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-160" due-to="Matthias Hummel">
+ Added two sample (binned comparison) ChiSquare test.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-167">
+ Modified NormalDistributionImpl.cumulativeProbablity to catch
+ MaxIterationsExceededException and return 0 or 1, resp. if the argument
+ is more than 20 standard deviations from the mean.
+ </action>
+ <action dev="brentworden" type="update" issue="MATH-170" due-to="David J. M. Karlsen">
+ Added SynchronizedDescriptiveStatistics class.
+ </action>
+ <action dev="brentworden" type="update" issue="MATH-154" due-to="Remi Arntzen">
+ Added addAndCheck, mulAndCheck, and subAndCheck MathUtils methods for
+ long integer arguments.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-171" due-to="Niall Pemberton">
+ Merged most functions from ComplexUtils into Complex class, added
+ static factory method to Complex.
+ </action>
+ <action dev="psteitz" type="update">
+ Deprecated abstract factory methods and made DescriptiveStatistics and
+ and SummaryStatistics concrete classes. Pushed implementations up
+ from DescriptiveStatisticsImpl, SummaryStatisticsImpl. Made
+ implementations of statistics configurable via setters.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-174">
+ Changed Mean.evaluate() to use a two-pass algorithm, improving accuracy
+ by exploiting the the fact that this method has access to the full
+ array of data values.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-175" due-to="Carl Anderson">
+ Added check and rescaling of expected counts to sum to sum of expected
+ counts if necessary in ChiSquare test.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-164">
+ Handle multiplication of Complex numbers with infinite parts specially.
+ </action>
+ <action dev="luc" type="update" issue="MATH-176" due-to="Kazuhiro Koshino">
+ Add errors guessing to least-squares estimators.
+ </action>
+ <action dev="luc" type="update" issue="MATH-179" due-to="Niall Pemberton">
+ Add tests for Fraction constructor using double parameter.
+ </action>
+ <action dev="psteitz" type="add" issue="MATH-173" due-to="Bob MacCallum">
+ Added one-way ANOVA implementation.
+ </action>
+ <action dev="luc" type="update" issue="MATH-181" due-to="Niall Pemberton">
+ Add Fraction constructor using max denominator value.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-182">
+ Add integer overflow checks in Fraction constructor using double parameter.
+ </action>
+ <action dev="luc" type="fix" issue="MATH-185">
+ Throw EOFException when using empty files with ValueServer in replay and
+ digest modes.
+ </action>
+ <action dev="luc" type="update" >
+ Added a equals and hash methods in MathUtils to check for double arrays
+ </action>
+ <action dev="luc" type="add" >
+ Added an angle normalization method in MathUtils to force angles in some
+ user-defined interval
+ </action>
+ <action dev="luc" type="add" >
+ Added vectorial covariance computation (either sample or population
+ covariance)
+ </action>
+ <action dev="luc" type="add" >
+ Added multivariate summary statistics.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-191">
+ Added getSumOfLogs method to SummaryStatistics and made SumOfLogs
+ instance used by GeometricMean configurable.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-184" due-to="Yegor Bryukhov">
+ Fixed AbstractIntegerDistribution cumulativeProbablility(-,-)
+ to correctly handle double arguments.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-188" due-to="Sebastian Bazley">
+ Made numerator and denominator final in Fraction and
+ deprecated protected real and imaginary parts fields in Complex,
+ making Fraction immutable and preparing Complex to become fully
+ immutable in 2.0.
+ </action>
+ </release>
+ <release version="1.1" date="2005-12-17"
+ description="This is a maintenance release containing bug fixes and enhancements.
+ All API changes are binary compatible with version 1.0. The enhancements
+ include some new probability distributions, a Fraction class, new matrix
+ and numerical utilities, and a PRNG pluggability framework making it
+ possible to replace the JDK-supplied random number generator in
+ commons-math (and elsewhere) with alternative PRNG implementations.">
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Made NewtonSolver derivative field transient and implemented readObject to
+ initialize.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Made sampleStats field private and changed getUpperBounds to return a fresh
+ copy in EmpiricalDistributionImpl.
+ </action>
+ <action dev="psteitz" type="update">
+ Added polar2Complex method to ComplexUtils to create Complex numbers
+ from polar representations.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-49" due-to="Elliotte Harold">
+ Made all serialVersionUIDs private.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-5">
+ Improved documentation and test cases related to handling of infinite
+ and NaN values in Complex, ComplexUtils classes.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-57" due-to="Mauro Talevi">
+ Fixed incorrect NaN handling in o.a.m.s.d.rank.Min, Max
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Changed RealMatrixImpl.equals to use Double.doubleToLongBits to compare
+ corresponding matrix entries.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Eliminated floating point equals comparison in Percentile.evaluate.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Eliminated unnecessary assignment statements in Skewness.getResult
+ method.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Synchronized getters in ResizeableDoubleArray.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Eliminated unnecessary assignment statement in BisectionSolver.solve
+ method.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-74">
+ Implemented hashCode in the Complex class and changed the semantics of
+ equals to make all instances with real or imaginary part NaN equal.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-12" due-to="Keith McDonald">
+ Fixed bin index overflow problem in EmpiricalDistributionImpl.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-108" due-to="Xiaogang Zhang">
+ Added protection against numerical overflow and underflow in the
+ isBracketing method.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-47" due-to="Nikhil Gupte">
+ Fixed division by zero error in rounding methods.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-100" due-to="Mike Hu">
+ Added upper tail cumulative probability method to HypergeometricDistributionImpl.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-22" due-to="Xiaogang Zhang">
+ Added better handling of numerical overflow and division by zero in
+ Complex calculations.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-92" due-to="Mikael Weigelt">
+ Changed ContinuedFraction to better handle infinite convergents that
+ resulted in divergent continued fraction evaluations.
+ </action>
+ <action dev="brentworden" type="fix" issue="MATH-32" due-to="Srinivas Vemury">
+ Changed rounding methods to not rely on BigDecimal conversions which
+ was causing numerical error.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-3" due-to="Jörg Weimar">
+ Changed Fraction(double) to correctly handle near-integral arguments.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-143" due-to="Jörg Weimar">
+ Changed lcm to throw ArithmeticException (instead of returning bogus
+ value) if the result is too large to store as an integer.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-70" due-to="Mary Ellen Foster">
+ Added factories for TTest, ChiSquareTest and TestUtils class with
+ static methods to create instances and execute tests.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-95" due-to="Paul Field">
+ Eliminated repeated endpoint function evalutations in BrentSolver, SecantSolver.
+ </action>
+ <action dev="psteitz" type="update" issue="MATH-137" due-to="Rodrigo di Lorenzo Lopes">
+ Added setSubMatrix methods to RealMatrixImpl, BigMatrixImpl. To
+ Preserve backward compatibility with version 1.0, these methods were
+ not added to the RealMatrix, BigMatrix interfaces.
+ </action>
+ <action dev="psteitz" type="update">
+ Added createXIdentityMatrix methods to MatrixUtils and deprecated
+ getIdentity methods in RealMatrixImpl, BigMatrixImpl.
+ Modified RealMatrixImpl, BigMatrixImpl constructors to throw
+ IllegalArgumentExceptions instead of ArrayIndexOutOfBounds when
+ dimension arguments are not positive.
+ </action>
+ <action dev="psteitz" type="update">
+ Made PRNG pluggable for classes in the random package. Added
+ RandomGenerator interface extracted from java.util.random and abstract
+ implementation, AbstractRandomGenerator providing default
+ implementations of methods based on nextDouble(). Added a constructor
+ taking a RandomGenerator as an argument to RandomDataImpl. Changed
+ ValueServer to use a RandomData in its constructor. Changes to
+ 1.0 classes should be backward compatible (including serialization).
+ </action>
+ <action dev="psteitz" type="update" due-to="C. Scott Ananian">
+ Added utility methods for overflow-checked integer arithmetic and
+ improved gcd method in MathUtils.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-79" due-to="Gilles Gaillard">
+ Fixed error in TTestImpl.homoscedasticTtest. Implementation was
+ incorrectly using heteroscedastic t statistic. Also improved
+ sensitivity of test cases.
+ </action>
+ <action dev="psteitz" type="fix" issue="MATH-44" due-to="Gilles Gaillard">
+ Fixed javadoc errors. One-sided t-test significance adjustment was
+ reversed in javadoc for boolean-valued test methods.
+ </action>
+ <action dev="brentworden" type="fix" due-to="Ben Litchfield">
+ Fixed bug in PolynomialSplineFunction to allow evaluation of the
+ function at the last knot point.
+ </action>
+ <action dev="brentworden" type="add">
+ Added Weibull distribution implementation.
+ </action>
+ <action dev="brentworden" type="add">
+ Added Cauchy distribution implementation.
+ </action>
+ <action dev="brentworden" type="add">
+ Added convenience methods for rounding.
+ </action>
+ <action dev="brentworden" type="add" due-to="C. Scott Ananian">
+ Added Fraction class based on commons-lang implementation. With the
+ fraction class, FractionFormat and ProperFractionFormat classes were
+ added to provide fraction formatting and parsing.
+ </action>
+ </release>
+ <release version="1.0" date="2004-12-06"
+ description="Apache Commons Math 1.0 - Initial Release">
+ </release>
+ </body>
+</document>
diff --git a/src/site/xdoc/developers.xml b/src/site/xdoc/developers.xml
new file mode 100644
index 0000000..17f07b1
--- /dev/null
+++ b/src/site/xdoc/developers.xml
@@ -0,0 +1,311 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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: 804554 $ $Date: 2009-08-16 04:52:32 +0200 (dim. 16 août 2009) $ -->
+<document>
+ <properties>
+ <title>Developers Guide</title>
+ <author email="rdonkin at apache.org">Robert Burrell Donkin</author>
+ </properties>
+
+ <body>
+ <section name="Aims">
+ <p>
+ Creating and maintaining a mathematical and statistical library that is
+ accurate requires a greater degree of communication than might be the
+ case for other components. It is important that developers follow
+ guidelines laid down by the community to ensure that the code they create
+ can be successfully maintained by others.
+ </p>
+ </section>
+ <section name='Guidelines'>
+ <p>
+ Developers are asked to comply with the following development guidelines.
+ Code that does not comply with the guidelines including the word <i>must</i>
+ will not be committed. Our aim will be to fix all of the exceptions to the
+ "<i>should</i>" guidelines prior to a release.
+ </p>
+ <subsection name="Contributing">
+ <p><strong>Getting Started</strong></p>
+ <p>
+ <ol>
+ <li>Start by reviewing the overall objectives stated in the
+ <a href="proposal.html">proposal</a> upon which the project is
+ founded.
+ </li>
+ <li>Download the commons math source code. Follow the instructions
+ under the heading "Anonymous Subversion" on the
+ <a href="http://www.apache.org/dev/version-control.html">Apache version
+ control page</a> (Also have a look at the
+ <a href="http://wiki.apache.org/commons/UsingSVN">Commons wiki
+ svn page </a>) to check out the commons math code base from Subversion.
+ The svn url for the current development sources of commons-math
+ is
+<source>https://svn.apache.org/repos/asf/commons/proper/math/trunk</source>
+ </li>
+ <li>Like most commons components, commons-math uses Apache Maven as our
+ build tool. We now use Maven 2 as our primary build platform (what
+ we use to cut releases, for continuous integration builds, and for
+ development). The sources can also be built using Ant (a working
+ Ant build.xml is included in the top level project directory).
+ To build commons math using Maven 2, you can follow the instructions for
+ <a href="http://maven.apache.org/run-maven/index.html">Building a
+ project with Maven 2</a>. Launch Maven from the top-level directory
+ in the checkout of commons-math trunk. No special setup is required,
+ except that currently to build the site (i.e. to execute Maven's
+ "site" goal), you may need to increase the default memory allocation
+ (e.g. <code>export MAVEN_OPTS=-Xmx512m</code>) before launching
+ Maven.
+ </li>
+ <li>Have a look at the new features that users and developers have requested
+ on the <a href="http://wiki.apache.org/commons/MathWishList">
+ Math Wish List Wiki Page.</a>
+ </li>
+ <li>Be sure to join the commons-dev and commons-user
+ <a href="mail-lists.html">
+ email lists</a> and use them appropriately (make sure the string
+ "[math]" starts the Subject line of all your postings).
+ Make any proposals here where the group can comment on them.
+ </li>
+ <li>
+ <a href="https://issues.apache.org/jira/secure/Signup!default.jspa">
+ Setup an account on JIRA</a> and use it to submit patches and
+ identify bugs. Read the
+ <a href="http://issues.apache.org/bugwritinghelp.html">
+ directions</a> for submitting bugs and search the database to
+ determine if an issue exists or has already been dealt with.
+ <p>
+ See the <a href="http://commons.apache.org/math/issue-tracking.html">
+ Commons Math Issue Tracking Page</a> for more information on how to
+ search for or submit bugs or enhancement requests.
+ </p>
+ <li>
+ Generating patches: The requested format for generating patches is
+ the Unified Diff format, which can be easily generated using the svn
+ client or various IDEs.
+<source>svn diff > patch </source>
+ </li>
+ </li>
+ </ol>
+ </p>
+ <p><strong>Contributing ideas and code</strong></p>
+ <p>
+ Follow the steps below when making suggestions for additions or
+ enhancements to commons-math. This will make it easier for the community
+ to comment on your ideas and for the committers to keep track of them.
+ Thanks in advance!
+ <ol>
+ <li>Start with a post to the commons-dev mailing list, with [math] at
+ the beginning of the subject line, followed by a good, short title
+ describing the new feature or enhancement. For example, "[math]
+ Principal Components Analysis." The body of the post should include each
+ of the following items (but be <strong>as brief as possible</strong>):
+ <ul>
+ <li>A concise description of the new feature / enhancement</li>
+ <li>References to definitions and algorithms. Using standard
+ definitions and algorithms makes communication much easier and will
+ greatly increase the chances that we will accept the code / idea</li>
+ <li>Some indication of why the addition / enhancement is practically
+ useful</li>
+ </ul></li>
+ <li>Assuming a generally favorable response to the idea on commons-dev,
+ the next step is to add an entry to the
+ <a href="http://wiki.apache.org/commons/MathWishList">Math Wish
+ List</a> corresponding to the idea. Include a reference to the
+ discussion thread and, for substantial enhancements, a new Wiki page
+ named using the enhancement / addition name, e.g.
+ "PrincipalComponentsAnalysis." We can then us this page to lay out the
+ development plan and track progress and decisions related to the
+ feature.</li>
+ <li>Create a JIRA ticket using the the feature title as the short
+ description. Incorporate feedback from the initial posting in the
+ description. Add a reference to the JIRA ticket to the WishList entry.
+ </li>
+ <li>Submit code as attachments to the JIRA ticket. Please use one
+ ticket for each feature, adding multiple patches to the ticket
+ as necessary. Use the svn diff command to generate your patches as
+ diffs. Please do not submit modified copies of existing java files. Be
+ patient (but not <strong>too</strong> patient) with committers reviewing
+ patches. Post a *nudge* message to commons-dev with a reference to the
+ ticket if a patch goes more than a few days with no comment or commit.
+ </li>
+ </ol>
+ </p>
+ </subsection>
+ <subsection name='Coding Style'>
+ <p>
+ Commons-math follows <a href="http://java.sun.com/docs/codeconv/">Code
+ Conventions for the Java Programming Language</a>. As part of the maven
+ build process, style checking is performed using the Checkstyle plugin,
+ using the properties specified in <code>checkstyle.xml</code>.
+ Committed code <i>should</i> generate no Checkstyle errors. One thing
+ that Checkstyle will complain about is tabs included in the source code.
+ Please make sure to set your IDE or editor to use spaces instead of tabs.
+ </p>
+ <p>
+ Committers should make sure that svn properties are correctly set on
+ files added to the repository. See the section on Committer Subversion
+ Access on the <a href="http://www.apache.org/dev/version-control.html">
+ Apache Source Code Repositories</a> page.
+ </p>
+ </subsection>
+ <subsection name='Documentation'>
+ <ul>
+ <li>
+ Committed code <i>must</i> include full javadoc.</li>
+ <li>
+ All component contracts <i>must</i> be fully specified in the javadoc class,
+ interface or method comments, including specification of acceptable ranges
+ of values, exceptions or special return values.</li>
+ <li>
+ External references or full statements of definitions for all mathematical
+ terms used in component documentation <i>must</i> be provided.</li>
+ <li>
+ Implementations <i>should</i> use standard algorithms and
+ references or full descriptions of all algorithms <i>should</i> be
+ provided.</li>
+ <li>
+ Additions and enhancements <i>should</i> include updates to the User
+ Guide.</li>
+ </ul>
+ </subsection>
+ <subsection name='Unit Tests'>
+ <ul>
+ <li>
+ Committed code <i>must</i> include unit tests.</li>
+ <li>
+ Unit tests <i>should</i> provide full path coverage. </li>
+ <li>
+ Unit tests <i>should</i> verify all boundary conditions specified in
+ interface contracts, including verification that exceptions are thrown or
+ special values (e.g. Double.NaN, Double.Infinity) are returned as
+ expected. </li>
+ </ul>
+ </subsection>
+ <subsection name='Licensing and copyright'>
+ <ul>
+ <li>
+ All new source file submissions <i>must</i> include the Apache Software
+ License in a comment that begins the file </li>
+ <li>
+ All contributions must comply with the terms of the Apache
+ <a href="http://www.apache.org/licenses/cla.pdf">Contributor License
+ Agreement (CLA)</a></li>
+ <li>
+ Patches <i>must</i> be accompanied by a clear reference to a "source"
+ - if code has been "ported" from another language, clearly state the
+ source of the original implementation. If the "expression" of a given
+ algorithm is derivative, please note the original source (textbook,
+ paper, etc.).</li>
+ <li>
+ References to source materials covered by restrictive proprietary
+ licenses should be avoided. In particular, contributions should not
+ implement or include references to algorithms in
+ <a href="http://www.nr.com/">Numerical Recipes (NR)</a>.
+ Any questions about copyright or patent issues should be raised on
+ the commons-dev mailing list before contributing or committing code.
+ </li>
+ </ul>
+ </subsection>
+ </section>
+ <section name='Recommended Readings'>
+ <p>
+ Here is a list of relevant materials. Much of the discussion surrounding
+ the development of this component will refer to the various sources
+ listed below, and frequently the Javadoc for a particular class or
+ interface will link to a definition contained in these documents.
+ </p>
+ <subsection name='Recommended Readings'>
+ <dl>
+ <dt>Concerning floating point arithmetic.</dt>
+ <dd>
+ <a href="http://www.validlab.com/goldberg/paper.ps">
+ http://www.validlab.com/goldberg/paper.ps
+ </a><br/>
+ <a href="http://www.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps">
+ http://www.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps
+ </a><br/>
+ <a href="http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf">
+ http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf
+ </a><br/>
+ </dd>
+ <dt>Numerical analysis</dt>
+ <dd>
+ <a href="http://www.mathcom.com/corpdir/techinfo.mdir/scifaq/index.html">
+ Scientific Computing FAQ @ Mathcom
+ </a><br/>
+ <a href="http://www.ma.man.ac.uk/~higham/asna/asna2.pdf">
+ Bibliography of accuracy and stability of numerical algorithms
+ </a><br/>
+ <a href="http://tonic.physics.sunysb.edu/docs/num_meth.html">
+ SUNY Stony Brook numerical methods page
+ </a><br/>
+ <a href="http://epubs.siam.org/sam-bin/dbq/toclist/SINUM">
+ SIAM Journal of Numerical Analysis Online
+ </a><br/>
+ </dd>
+ <dt>Probability and statistics</dt>
+ <dd>
+ <a href="http://lib.stat.cmu.edu/">
+ Statlib at CMU
+ </a><br/>
+ <a href="http://www.itl.nist.gov/div898/handbook/">
+ NIST Engineering Statistics Handbook
+ </a><br/>
+ <a href="http://www.psychstat.smsu.edu/sbk00.htm">
+ Online Introductory Statistics (David W. Stockburger)
+ </a><br/>
+ <a href="http://www.jstatsoft.org/">
+ Online Journal of Statistical Software
+ </a><br/>
+ </dd>
+ </dl>
+ </subsection>
+ <subsection name='Javadoc comment resources'>
+ <dl>
+ <dt>References for mathematical definitions.</dt>
+ <dd>
+ <a href="http://rd11.web.cern.ch/RD11/rkb/titleA.html">
+ http://rd11.web.cern.ch/RD11/rkb/titleA.html
+ </a><br/>
+ <a href="http://mathworld.wolfram.com/">
+ http://mathworld.wolfram.com/
+ </a><br/>
+ <a href="http://www.itl.nist.gov/div898/handbook">
+ http://www.itl.nist.gov/div898/handbook
+ </a><br/>
+ <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><br/>
+ </dd>
+ </dl>
+ </subsection>
+ <subsection name='XML'>
+ <dl>
+ <dt>XML related resources.</dt>
+ <dd>
+ <a href="http://www.openmath.org">
+ http://www.openmath.org
+ </a><br/>
+ </dd>
+ </dl>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/download_math.xml b/src/site/xdoc/download_math.xml
new file mode 100644
index 0000000..9edec91
--- /dev/null
+++ b/src/site/xdoc/download_math.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |**** ****|
+ |**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
+ |**** DO NOT EDIT DIRECTLY ****|
+ |**** ****|
+ +======================================================================+
+ | TEMPLATE FILE: download-page-template.xml |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ | |
+ | 1) Re-generate using: mvn commons:download-page |
+ | |
+ | 2) Set the following properties in the component's pom: |
+ | - commons.componentid (required, alphabetic, lower case) |
+ | - commons.release.version (required) |
+ | - commons.binary.suffix (optional) |
+ | (defaults to "-bin", set to "" for pre-maven2 releases) |
+ | |
+ | 3) Example Properties |
+ | |
+ | <properties> |
+ | <commons.componentid>math</commons.componentid> |
+ | <commons.release.version>1.2</commons.release.version> |
+ | </properties> |
+ | |
+ +======================================================================+
+-->
+<document>
+ <properties>
+ <title>Download Commons Math</title>
+ <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+ <body>
+ <section name="Download Commons Math">
+ <subsection name="Using a Mirror">
+ <p>
+ We recommend you use a mirror to download our release
+ builds, but you <strong>must</strong> verify the integrity of
+ the downloaded files using signatures downloaded from our main
+ distribution directories. Recent releases (48 hours) may not yet
+ be available from the mirrors.
+ </p>
+
+ <p>
+ You are currently using <b>[preferred]</b>. If you
+ encounter a problem with this mirror, please select another
+ mirror. If all mirrors are failing, there are <i>backup</i>
+ mirrors (at the end of the mirrors list) that should be
+ available.
+ <br></br>
+ [if-any logo]<a href="[link]"><img align="right" src="[logo]" border="0"></img></a>[end]
+ </p>
+
+ <form action="[location]" method="get" id="SelectMirror">
+ <p>
+ Other mirrors:
+ <select name="Preferred">
+ [if-any http]
+ [for http]<option value="[http]">[http]</option>[end]
+ [end]
+ [if-any ftp]
+ [for ftp]<option value="[ftp]">[ftp]</option>[end]
+ [end]
+ [if-any backup]
+ [for backup]<option value="[backup]">[backup] (backup)</option>[end]
+ [end]
+ </select>
+ <input type="submit" value="Change"></input>
+ </p>
+ </form>
+
+ <p>
+ The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
+ link links to the code signing keys used to sign the product.
+ The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site.
+ The <code>MD5</code> link downloads the checksum from the main site.
+ </p>
+ </subsection>
+ </section>
+ <section name="Commons Math 2.2 ">
+ <subsection name="Binaries">
+ <table>
+ <tr>
+ <td><a href="[preferred]/commons/math/binaries/commons-math-2.2.tar.gz">commons-math-2.2.tar.gz</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/binaries/commons-math-2.2.tar.gz.md5">md5</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/binaries/commons-math-2.2.tar.gz.asc">pgp</a></td>
+ </tr>
+ <tr>
+ <td><a href="[preferred]/commons/math/binaries/commons-math-2.2.zip">commons-math-2.2.zip</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/binaries/commons-math-2.2.zip.md5">md5</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/binaries/commons-math-2.2.zip.asc">pgp</a></td>
+ </tr>
+ </table>
+ </subsection>
+ <subsection name="Source">
+ <table>
+ <tr>
+ <td><a href="[preferred]/commons/math/source/commons-math-2.2-src.tar.gz">commons-math-2.2-src.tar.gz</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/source/commons-math-2.2-src.tar.gz.md5">md5</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/source/commons-math-2.2-src.tar.gz.asc">pgp</a></td>
+ </tr>
+ <tr>
+ <td><a href="[preferred]/commons/math/source/commons-math-2.2-src.zip">commons-math-2.2-src.zip</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/source/commons-math-2.2-src.zip.md5">md5</a></td>
+ <td><a href="http://www.apache.org/dist/commons/math/source/commons-math-2.2-src.zip.asc">pgp</a></td>
+ </tr>
+ </table>
+ </subsection>
+ </section>
+ <section name="Archives">
+ <p>
+ Older releases can be obtained from the archives.
+ </p>
+ <ul>
+ <li class="download"><a href="[preferred]/commons/math/">browse download area</a></li>
+ <li><a href="http://archive.apache.org/dist/commons/math/">archives...</a></li>
+ </ul>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
new file mode 100644
index 0000000..3368cde
--- /dev/null
+++ b/src/site/xdoc/index.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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: 923339 $ $Date: 2010-03-15 18:04:53 +0100 (lun. 15 mars 2010) $ -->
+<document>
+
+ <properties>
+ <title>Commons-Math: The Apache Commons Mathematics Library</title>
+ </properties>
+
+ <body>
+
+ <section name="Commons-Math: The Apache Commons Mathematics Library" href="summary">
+ <p>
+ Commons Math is a library of lightweight, self-contained
+ mathematics and statistics components addressing the most common
+ problems not available in the Java programming language or Commons
+ Lang.
+ </p>
+ <p>
+ Guiding principles:
+ <ol>
+ <li>
+ Real-world application use cases determine development
+ priority.
+ </li>
+ <li>
+ This package emphasizes small, easily integrated components
+ rather than large libraries with complex dependencies and
+ configurations.
+ </li>
+ <li>
+ All algorithms are fully documented and follow generally
+ accepted best practices.
+ </li>
+ <li>
+ In situations where multiple standard algorithms exist, a
+ Strategy pattern is used to support multiple
+ implementations.
+ </li>
+ <li>
+ Limited dependencies. No external dependencies beyond Commons
+ components and the core Java platform (at least Java 1.3 up to
+ version 1.2 of the library, at least Java 5 starting with version
+ 2.0 of the library).
+ </li>
+ </ol>
+ </p>
+ </section>
+ <section name="Download Math">
+ <subsection name="Releases">
+ <p>
+ Download the
+ <a href="http://commons.apache.org/math/download_math.cgi">
+ Latest Release</a> of Commons Math.
+ </p>
+ </subsection>
+ <!--
+ <subsection name="Nightly Builds">
+ <p>
+ <a href="http://people.apache.org/builds/commons/nightly/commons-math/">
+ Nightly builds</a> are built once a day from the current SVN HEAD.
+ This is (nearly) the latest code and so should be treated with
+ caution!
+ </p>
+ </subsection>
+ -->
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/issue-tracking.xml b/src/site/xdoc/issue-tracking.xml
new file mode 100644
index 0000000..237c4c3
--- /dev/null
+++ b/src/site/xdoc/issue-tracking.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |**** ****|
+ |**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
+ |**** DO NOT EDIT DIRECTLY ****|
+ |**** ****|
+ +======================================================================+
+ | TEMPLATE FILE: issue-tracking-template.xml |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ | |
+ | 1) Re-generate using: mvn commons:jira-page |
+ | |
+ | 2) Set the following properties in the component's pom: |
+ | - commons.jira.id (required, alphabetic, upper case) |
+ | - commons.jira.pid (required, numeric) |
+ | |
+ | 3) Example Properties |
+ | |
+ | <properties> |
+ | <commons.jira.id>MATH</commons.jira.id> |
+ | <commons.jira.pid>12310485</commons.jira.pid> |
+ | </properties> |
+ | |
+ +======================================================================+
+-->
+<document>
+ <properties>
+ <title>Commons Math Issue tracking</title>
+ <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+ <body>
+
+ <section name="Commons Math Issue tracking">
+ <p>
+ Commons Math uses <a href="http://issues.apache.org/jira/">ASF JIRA</a> for tracking issues.
+ See the <a href="http://issues.apache.org/jira/browse/MATH">Commons Math JIRA project page</a>.
+ </p>
+
+ <p>
+ To use JIRA you may need to <a href="http://issues.apache.org/jira/secure/Signup!default.jspa">create an account</a>
+ (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically
+ created and you can use the <a href="http://issues.apache.org/jira/secure/ForgotPassword!default.jspa">Forgot Password</a>
+ page to get a new password).
+ </p>
+
+ <p>
+ If you would like to report a bug, or raise an enhancement request with
+ Commons Math please do the following:
+ <ol>
+ <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310485&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">Search existing open bugs</a>.
+ If you find your issue listed then please add a comment with your details.</li>
+ <li><a href="mail-lists.html">Search the mailing list archive(s)</a>.
+ You may find your issue or idea has already been discussed.</li>
+ <li>Decide if your issue is a bug or an enhancement.</li>
+ <li>Submit either a <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310485&issuetype=1&priority=4&assignee=-1">bug report</a>
+ or <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310485&issuetype=4&priority=4&assignee=-1">enhancement request</a>.</li>
+ </ol>
+ </p>
+
+ <p>
+ Please also remember these points:
+ <ul>
+ <li>the more information you provide, the better we can help you</li>
+ <li>test cases are vital, particularly for any proposed enhancements</li>
+ <li>the developers of Commons Math are all unpaid volunteers</li>
+ </ul>
+ </p>
+
+ <p>
+ For more information on subversion and creating patches see the
+ <a href="http://www.apache.org/dev/contributors.html">Apache Contributors Guide</a>.
+ </p>
+
+ <p>
+ You may also find these links useful:
+ <ul>
+ <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310485&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">All Open Commons Math bugs</a></li>
+ <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310485&sorter/field=issuekey&sorter/order=DESC&status=5&status=6">All Resolved Commons Math bugs</a></li>
+ <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310485&sorter/field=issuekey&sorter/order=DESC">All Commons Math bugs</a></li>
+ </ul>
+ </p>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/mail-lists.xml b/src/site/xdoc/mail-lists.xml
new file mode 100644
index 0000000..6649d6d
--- /dev/null
+++ b/src/site/xdoc/mail-lists.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |**** ****|
+ |**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
+ |**** DO NOT EDIT DIRECTLY ****|
+ |**** ****|
+ +======================================================================+
+ | TEMPLATE FILE: mail-lists-template.xml |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ | |
+ | 1) Re-generate using: mvn commons:mail-page |
+ | |
+ | 2) Set the following properties in the component's pom: |
+ | - commons.componentid (required, alphabetic, lower case) |
+ | |
+ | 3) Example Properties |
+ | |
+ | <properties> |
+ | <commons.componentid>math</commons.componentid> |
+ | </properties> |
+ | |
+ +======================================================================+
+-->
+<document>
+ <properties>
+ <title>Commons Math Mailing Lists</title>
+ <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+ <body>
+
+ <section name="Overview">
+ <p>
+ <a href="index.html">Commons Math</a> shares mailing lists with all the other
+ <a href="http://commons.apache.org/components.html">Commons Components</a>.
+ To make it easier for people to only read messages related to components they are interested in,
+ the convention in Commons is to prefix the subject line of messages with the component's name,
+ for example:
+ <ul>
+ <li>[math] Problem with the ...</li>
+ </ul>
+ </p>
+ <p>
+ Questions related to the usage of Commons Math should be posted to the
+ <a href="http://mail-archives.apache.org/mod_mbox/commons-user/">User List</a>.
+ <br />
+ The <a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">Developer List</a>
+ is for questions and discussion related to the development of Commons Math.
+ <br />
+ Please do not cross-post; developers are also subscribed to the user list.
+ </p>
+ <p>
+ <strong>Note:</strong> please don't send patches or attachments to any of the mailing lists.
+ Patches are best handled via the <a href="issue-tracking.html">Issue Tracking</a> system.
+ Otherwise, please upload the file to a public server and include the URL in the mail.
+ </p>
+ </section>
+
+ <section name="Commons Math Mailing Lists">
+ <p>
+ <strong>Please prefix the subject line of any messages for <a href="index.html">Commons Math</a>
+ with <i>[math]</i></strong> - <i>thanks!</i>
+ <br />
+ <br />
+ </p>
+
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Subscribe</th>
+ <th>Unsubscribe</th>
+ <th>Post</th>
+ <th>Archive</th>
+ <th>Other Archives</th>
+ </tr>
+
+
+ <tr>
+ <td>
+ <strong>Commons User List</strong>
+ <br /><br />
+ Questions on using Commons Math.
+ <br /><br />
+ </td>
+ <td><a href="mailto:user-subscribe at commons.apache.org">Subscribe</a></td>
+ <td><a href="mailto:user-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+ <td><a href="mailto:user at commons.apache.org?subject=[math]">Post</a></td>
+ <td><a href="http://mail-archives.apache.org/mod_mbox/commons-user/">mail-archives.apache.org</a></td>
+ <td><a href="http://markmail.org/list/org.apache.commons.users/">markmail.org</a><br />
+ <a href="http://www.mail-archive.com/user@commons.apache.org/">www.mail-archive.com</a><br />
+ <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td>
+ <strong>Commons Developer List</strong>
+ <br /><br />
+ Discussion of development of Commons Math.
+ <br /><br />
+ </td>
+ <td><a href="mailto:dev-subscribe at commons.apache.org">Subscribe</a></td>
+ <td><a href="mailto:dev-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+ <td><a href="mailto:dev at commons.apache.org?subject=[math]">Post</a></td>
+ <td><a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">mail-archives.apache.org</a></td>
+ <td><a href="http://markmail.org/list/org.apache.commons.dev/">markmail.org</a><br />
+ <a href="http://www.mail-archive.com/dev@commons.apache.org/">www.mail-archive.com</a><br />
+ <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td>
+ <strong>Commons Issues List</strong>
+ <br /><br />
+ Only for e-mails automatically generated by the <a href="issue-tracking.html">issue tracking</a> system.
+ <br /><br />
+ </td>
+ <td><a href="mailto:issues-subscribe at commons.apache.org">Subscribe</a></td>
+ <td><a href="mailto:issues-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+ <td><i>read only</i></td>
+ <td><a href="http://mail-archives.apache.org/mod_mbox/commons-issues/">mail-archives.apache.org</a></td>
+ <td><a href="http://markmail.org/list/org.apache.commons.issues/">markmail.org</a><br />
+ <a href="http://www.mail-archive.com/issues@commons.apache.org/">www.mail-archive.com</a>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td>
+ <strong>Commons Commits List</strong>
+ <br /><br />
+ Only for e-mails automatically generated by the <a href="source-repository.html">source control</a> sytem.
+ <br /><br />
+ </td>
+ <td><a href="mailto:commits-subscribe at commons.apache.org">Subscribe</a></td>
+ <td><a href="mailto:commits-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+ <td><i>read only</i></td>
+ <td><a href="http://mail-archives.apache.org/mod_mbox/commons-commits/">mail-archives.apache.org</a></td>
+ <td><a href="http://markmail.org/list/org.apache.commons.commits/">markmail.org</a><br />
+ <a href="http://www.mail-archive.com/commits@commons.apache.org/">www.mail-archive.com</a>
+ </td>
+ </tr>
+
+ </table>
+
+ </section>
+ <section name="Apache Mailing Lists">
+ <p>
+ Other mailing lists which you may find useful include:
+ </p>
+
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Subscribe</th>
+ <th>Unsubscribe</th>
+ <th>Post</th>
+ <th>Archive</th>
+ <th>Other Archives</th>
+ </tr>
+ <tr>
+ <td>
+ <strong>Apache Announce List</strong>
+ <br /><br />
+ General announcements of Apache project releases.
+ <br /><br />
+ </td>
+ <td><a class="externalLink" href="mailto:announce-subscribe at apache.org">Subscribe</a></td>
+ <td><a class="externalLink" href="mailto:announce-unsubscribe at apache.org">Unsubscribe</a></td>
+ <td><i>read only</i></td>
+ <td><a class="externalLink" href="http://mail-archives.apache.org/mod_mbox/announce/">mail-archives.apache.org</a></td>
+ <td><a class="externalLink" href="http://markmail.org/list/org.apache.announce/">markmail.org</a><br />
+ <a class="externalLink" href="http://old.nabble.com/Apache-News-and-Announce-f109.html">old.nabble.com</a><br />
+ <a class="externalLink" href="http://www.mail-archive.com/announce@apache.org/">www.mail-archive.com</a><br />
+ <a class="externalLink" href="http://news.gmane.org/gmane.comp.apache.announce">news.gmane.org</a>
+ </td>
+ </tr>
+ </table>
+
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/proposal.xml b/src/site/xdoc/proposal.xml
new file mode 100644
index 0000000..1e2b160
--- /dev/null
+++ b/src/site/xdoc/proposal.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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: 799967 $ $Date: 2009-08-02 03:06:03 +0200 (dim. 02 août 2009) $ -->
+<document>
+
+ <properties>
+ <title>Proposal for math Package</title>
+ <author email="martin at mvdb.net">Robert Burrell Donkin</author>
+ </properties>
+
+ <body>
+
+ <section name='Proposal for math Package'>
+
+ <subsection name='(0) Rationale'>
+<p>The Java programming language and the math extensions in commons-lang provide implementations
+for only the most basic mathematical algorithms. Routine development tasks such as computing
+basic statistics or solving a system of linear equations require components not available in java
+or commons-lang.</p>
+
+<p>Most basic mathematical or statistical algorithms are available in open source implementations,
+but to assemble a simple set of capabilities one has to use multiple libraries, many of which have
+more restrictive licensing terms than the ASF. In addition, many of the best open source
+implementations (e.g. the R statistical package) are either not available in Java or require large
+support libraries and/or external dependencies to work.</p>
+
+<p>A commons-math community will provide a productive environment for aggregation, testing and
+support of efficient Java implementations of commonly used mathematical and statistical algorithms.</p>
+
+ </subsection>
+
+ <subsection name='(1) Scope of the Package'>
+
+<p>The Math project shall create and maintain a library of lightweight, self-contained mathematics
+and statistics components addressing the most common practical problems not immediately available in
+the Java programming language or commons-lang. The guiding principles for commons-math will be:
+
+<ol>
+<li>Real-world application use cases determine priority</li>
+<li>Emphasis on small, easily integrated components rather than large libraries with complex
+dependencies</li>
+<li>All algorithms are fully documented and follow generally accepted best practices</li>
+<li>In situations where multiple standard algorithms exist, use the Strategy pattern to support
+multiple implementations</li>
+<li>Limited dependencies. No external dependencies beyond Commons components and the JDK</li>
+</ol>
+</p>
+ </subsection>
+
+ <subsection name='(1.5) Interaction With Other Packages'>
+
+<p><em>math</em> relies only on standard JDK 1.2 (or later) APIs for
+production deployment. It utilizes the JUnit unit testing framework for
+developing and executing unit tests, but this is of interest only to
+developers of the component.</p>
+
+<p>No external configuration files are utilized.</p>
+
+ </subsection>
+ <subsection name='(2) Initial Source of the Package'>
+
+<p>The initial codebase will consist of implementations of basic statistical algorithms such
+as the following:
+<ul>
+<li>Simple univariate statistics (mean, standard deviation, n, confidence intervals)</li>
+<li>Frequency distributions</li>
+<li>t-test, chi-square test</li>
+<li>Random numbers from Gaussian, Exponential, Poisson distributions</li>
+<li>Random sampling/resampling</li>
+<li>Bivariate regression, corellation</li>
+</ul>
+
+and mathematical algorithms such as the following:
+<ul>
+<li>Basic Complex Number representation with algebraic operations</li>
+<li>Newton's method for finding roots</li>
+<li>Binomial coefficients</li>
+<li>Exponential growth and decay (set up for financial applications)</li>
+<li>Polynomial Interpolation (curve fitting)</li>
+<li>Basic Matrix representation with algebraic operations</li>
+</ul>
+</p>
+
+<p>The proposed package name for the new component is
+<code>org.apache.commons.math</code>.</p>
+
+ </subsection>
+ <subsection name='(3) Required Jakarta-Commons Resources'>
+
+<ul>
+<li>CVS Repository - New directory <code>math</code> in the
+ <code>jakarta-commons</code> CVS repository.</li>
+<li>Mailing List - Discussions will take place on the general
+ <em>dev at commons.apache.org</em> mailing list. To help
+ list subscribers identify messages of interest, it is suggested that
+ the message subject of messages about this component be prefixed with
+ [math].</li>
+<li>Bugzilla - New component "math" under the "Commons" product
+ category, with appropriate version identifiers as needed.</li>
+<li>Jyve FAQ - New category "commons-math" (when available).</li>
+</ul>
+
+ </subsection>
+ <subsection name='(4) Initial Committers'>
+
+<p>The initial committers on the math component shall be:
+<ul>
+ <li><a href="mailto:rdonkin at apache.org">Robert Burrell Donkin</a></li>
+ <li><a href="mailto:tobrien at apache.org">Tim O'Brien</a></li>
+</ul>
+</p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/analysis.xml b/src/site/xdoc/userguide/analysis.xml
new file mode 100644
index 0000000..903bb5f
--- /dev/null
+++ b/src/site/xdoc/userguide/analysis.xml
@@ -0,0 +1,413 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937545 $ $Date: 2010-04-24 00:33:37 +0200 (sam. 24 avril 2010) $ -->
+<document url="analysis.html">
+ <properties>
+ <title>The Commons Math User Guide - Numerical Analysis</title>
+ </properties>
+ <body>
+ <section name="4 Numerical Analysis">
+ <subsection name="4.1 Overview" href="overview">
+ <p>
+ The analysis package is the parent package for algorithms dealing with
+ real-valued functions of one real variable. It contains dedicated sub-packages
+ providing numerical root-finding, integration, and interpolation. It also
+ contains a polynomials sub-package that considers polynomials with real
+ coefficients as differentiable real functions.
+ </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>
+ <p>
+ Possible future additions may include numerical differentiation.
+ </p>
+ </subsection>
+ <subsection name="4.2 Root-finding" href="rootfinding">
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/analysis/solvers/UnivariateRealSolver.html">
+ UnivariateRealSolver</a> provides the means to find roots of
+ <a href="../apidocs/org/apache/commons/math/analysis/UnivariateRealFunction.html">univariate real-valued functions</a>.
+ A root is the value where the function takes the value 0. Commons-Math
+ includes implementations of the following root-finding algorithms: <ul>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/BisectionSolver.html">
+ Bisection</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/BrentSolver.html">
+ Brent-Dekker</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/NewtonSolver.html">
+ Newton's Method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/SecantSolver.html">
+ Secant Method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/MullerSolver.html">
+ Muller's Method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/LaguerreSolver.html">
+ Laguerre's Method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/solvers/RidderSolver.html">
+ Ridder's Method</a></li>
+ </ul>
+ </p>
+ <p>
+ There are numerous non-obvious traps and pitfalls in root finding.
+ First, the usual disclaimers due to the way real world computers
+ calculate values apply. If the computation of the function provides
+ numerical instabilities, for example due to bit cancellation, the root
+ finding algorithms may behave badly and fail to converge or even
+ return bogus values. There will not necessarily be an indication that
+ the computed root is way off the true value. Secondly, the root finding
+ problem itself may be inherently ill-conditioned. There is a
+ "domain of indeterminacy", the interval for which the function has
+ near zero absolute values around the true root, which may be large.
+ Even worse, small problems like roundoff error may cause the function
+ value to "numerically oscillate" between negative and positive values.
+ This may again result in roots way off the true value, without
+ indication. There is not much a generic algorithm can do if
+ ill-conditioned problems are met. A way around this is to transform
+ the problem in order to get a better conditioned function. Proper
+ selection of a root-finding algorithm and its configuration parameters
+ requires knowledge of the analytical properties of the function under
+ analysis and numerical analysis techniques. Users are encouraged
+ to consult a numerical analysis text (or a numerical analyst) when
+ selecting and configuring a solver.
+ </p>
+ <p>
+ In order to use the root-finding features, first a solver object must
+ be created. It is encouraged that all solver object creation occurs
+ via the <a href="../apidocs/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactory.html">
+ UnivariateRealSolverFactory</a> class. <code>UnivariateRealSolverFactory</code>
+ is a simple factory used to create all of the solver objects supported by
+ Commons-Math.
+ The typical usage of <code>UnivariateRealSolverFactory</code> to create a
+ solver object would be:
+ </p>
+ <source>UnivariateRealSolverFactory factory = UnivariateRealSolverFactory.newInstance();
+UnivariateRealSolver solver = factory.newDefaultSolver();</source>
+ <p>
+ The solvers that can be instantiated via the
+ <code>UnivariateRealSolverFactory</code> are detailed below:
+ <table>
+ <tr><th>Solver</th><th>Factory Method</th><th>Notes on Use</th></tr>
+ <tr><td>Bisection</td><td>newBisectionSolver</td><td><div>Root must be
+ bracketted.</div><div>Linear, guaranteed convergence</div></td></tr>
+ <tr><td>Brent</td><td>newBrentSolver</td><td><div>Root must be bracketted.</div>
+ <div>Super-linear, guaranteed convergence</div></td></tr>
+ <tr><td>Newton</td><td>newNewtonSolver</td><td><div>Uses single value for
+ initialization.</div><div>Super-linear, non-guaranteed convergence</div>
+ <div>Function must be differentiable</div></td></tr>
+ <tr><td>Secant</td><td>newSecantSolver</td><td><div>Root must be bracketted.</div>
+ <div>Super-linear, non-guaranteed convergence</div></td></tr>
+ <tr><td>Muller</td><td>newMullerSolver</td><td><div>Root must be bracketted.</div>
+ <div>We restrict ourselves to real valued functions, not complex ones</div>
+ </td></tr>
+ <tr><td>Laguerre</td><td>newLaguerreSolver</td><td><div>Root must be bracketted.</div>
+ <div>Function must be a polynomial</div></td></tr>
+ <tr><td>Ridder</td><td>newRidderSolver</td><td><div>Root must be bracketted.</div>
+ <div></div></td></tr>
+ </table>
+ </p>
+ <p>
+ Using a solver object, roots of functions are easily found using the <code>solve</code>
+ methods. For a function <code>f</code>, and two domain values, <code>min</code> and
+ <code>max</code>, <code>solve</code> computes a value <code>c</code> such that:
+ <ul>
+ <li><code>f(c) = 0.0</code> (see "function value accuracy")</li>
+ <li><code>min <= c <= max</code></li>
+ </ul>
+ </p>
+ <p>
+ Typical usage:
+ </p>
+ <source>UnivariateRealFunction function = // some user defined function object
+UnivariateRealSolverFactory factory = UnivariateRealSolverFactory.newInstance();
+UnivariateRealSolver solver = factory.newBisectionSolver();
+double c = solver.solve(function, 1.0, 5.0);</source>
+ <p>
+ The <code>BrentSolve</code> uses the Brent-Dekker algorithm which is
+ fast and robust. This algorithm is recommended for most users and the
+ <code>BrentSolver</code> is the default solver provided by the
+ <code>UnivariateRealSolverFactory</code>. If there are multiple roots
+ in the interval, or there is a large domain of indeterminacy, the
+ algorithm will converge to a random root in the interval without
+ indication that there are problems. Interestingly, the examined text
+ book implementations all disagree in details of the convergence
+ criteria. Also each implementation had problems for one of the test
+ cases, so the expressions had to be fudged further. Don't expect to
+ get exactly the same root values as for other implementations of this
+ algorithm.
+ </p>
+ <p>
+ The <code>SecantSolver</code> uses a variant of the well known secant
+ algorithm. It may be a bit faster than the Brent solver for a class
+ of well-behaved functions.
+ </p>
+ <p>
+ The <code>BisectionSolver</code> is included for completeness and for
+ establishing a fall back in cases of emergency. The algorithm is
+ simple, most likely bug free and guaranteed to converge even in very
+ adverse circumstances which might cause other algorithms to
+ malfunction. The drawback is of course that it is also guaranteed
+ to be slow.
+ </p>
+ <p>
+ The <code>UnivariateRealSolver</code> interface exposes many
+ properties to control the convergence of a solver. For the most part,
+ these properties should not have to change from their default values
+ to produce good results. In the circumstances where changing these
+ property values is needed, it is easily done through getter and setter
+ methods on <code>UnivariateRealSolver</code>:
+ <table>
+ <tr><th>Property</th><th>Methods</th><th>Purpose</th></tr>
+ <tr>
+ <td>Absolute accuracy</td>
+ <td>
+ <div>getAbsoluteAccuracy</div>
+ <div>resetAbsoluteAccuracy</div>
+ <div>setAbsoluteAccuracy</div>
+ </td>
+ <td>
+ The Absolute Accuracy is (estimated) maximal difference between
+ the computed root and the true root of the function. This is
+ what most people think of as "accuracy" intuitively. The default
+ value is chosen as a sane value for most real world problems,
+ for roots in the range from -100 to +100. For accurate
+ computation of roots near zero, in the range form -0.0001 to
+ +0.0001, the value may be decreased. For computing roots
+ much larger in absolute value than 100, the default absolute
+ accuracy may never be reached because the given relative
+ accuracy is reached first.
+ </td>
+ </tr>
+ <tr>
+ <td>Relative accuracy</td>
+ <td>
+ <div>getRelativeAccuracy</div>
+ <div>resetRelativeAccuracy</div>
+ <div>setRelativeAccuracy</div>
+ </td>
+ <td>
+ The Relative Accuracy is the maximal difference between the
+ computed root and the true root, divided by the maximum of the
+ absolute values of the numbers. This accuracy measurement is
+ better suited for numerical calculations with computers, due to
+ the way floating point numbers are represented. The default
+ value is chosen so that algorithms will get a result even for
+ roots with large absolute values, even while it may be
+ impossible to reach the given absolute accuracy.
+ </td>
+ </tr>
+ <tr>
+ <td>Function value accuracy</td>
+ <td>
+ <div>getFunctionValueAccuracy</div>
+ <div>resetFunctionValueAccuracy</div>
+ <div>setFunctionValueAccuracy</div>
+ </td>
+ <td>
+ This value is used by some algorithms in order to prevent
+ numerical instabilities. If the function is evaluated to an
+ absolute value smaller than the Function Value Accuracy, the
+ algorithms assume they hit a root and return the value
+ immediately. The default value is a "very small value". If the
+ goal is to get a near zero function value rather than an accurate
+ root, computation may be sped up by setting this value
+ appropriately.
+ </td>
+ </tr>
+ <tr>
+ <td>Maximum iteration count</td>
+ <td>
+ <div>getMaximumIterationCount</div>
+ <div>resetMaximumIterationCount</div>
+ <div>setMaximumIterationCount</div>
+ </td>
+ <td>
+ This is the maximal number of iterations the algorithm will try.
+ If this number is exceeded, non-convergence is assumed and a
+ <code>ConvergenceException</code> exception is thrown. The
+ default value is 100, which should be plenty, given that a
+ bisection algorithm can't get any more accurate after 52
+ iterations because of the number of mantissa bits in a double
+ precision floating point number. If a number of ill-conditioned
+ problems is to be solved, this number can be decreased in order
+ to avoid wasting time.
+ </td>
+ </tr>
+ </table>
+ </p>
+ </subsection>
+ <subsection name="4.3 Interpolation" href="interpolation">
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/analysis/interpolation/UnivariateRealInterpolator.html">
+ UnivariateRealInterpolator</a> is used to find a univariate real-valued
+ function <code>f</code> which for a given set of ordered pairs
+ (<code>x<sub>i</sub></code>,<code>y<sub>i</sub></code>) yields
+ <code>f(x<sub>i</sub>)=y<sub>i</sub></code> to the best accuracy possible. The result
+ is provided as an object implementing the <a
+ href="../apidocs/org/apache/commons/math/analysis/UnivariateRealFunction.html">
+ UnivariateRealFunction</a> interface. It can therefore be evaluated at any point,
+ including point not belonging to the original set.
+ Currently, only an interpolator for generating natural cubic splines and a polynomial
+ interpolator are available. There is no interpolator factory, mainly because the
+ interpolation algorithm is more determined by the kind of the interpolated function
+ rather than the set of points to interpolate.
+ There aren't currently any accuracy controls either, as interpolation
+ accuracy is in general determined by the algorithm.
+ </p>
+ <p>Typical usage:</p>
+ <source>double x[] = { 0.0, 1.0, 2.0 };
+double y[] = { 1.0, -1.0, 2.0);
+UnivariateRealInterpolator interpolator = new SplineInterpolator();
+UnivariateRealFunction function = interpolator.interpolate(x, y);
+double interpolationX = 0.5;
+double interpolatedY = function.evaluate(x);
+System.out println("f(" + interpolationX + ") = " + interpolatedY);</source>
+ <p>
+ A natural cubic spline is a function consisting of a polynomial of
+ third degree for each subinterval determined by the x-coordinates of the
+ interpolated points. A function interpolating <code>N</code>
+ value pairs consists of <code>N-1</code> polynomials. The function
+ is continuous, smooth and can be differentiated twice. The second
+ derivative is continuous but not smooth. The x values passed to the
+ interpolator must be ordered in ascending order. It is not valid to
+ evaluate the function for values outside the range
+ <code>x<sub>0</sub></code>..<code>x<sub>N</sub></code>.
+ </p>
+ <p>
+ The polynomial function returned by the Neville's algorithm is a single
+ polynomial guaranteed to pass exactly through the interpolation points.
+ The degree of the polynomial is the number of points minus 1 (i.e. the
+ interpolation polynomial for a three points set will be a quadratic
+ polynomial). Despite the fact the interpolating polynomials is a perfect
+ approximation of a function at interpolation points, it may be a loose
+ approximation between the points. Due to <a
+ href="http://en.wikipedia.org/wiki/Runge's_phenomenon">Runge's phenomenom</a>
+ the error can get worse as the degree of the polynomial increases, so
+ adding more points does not always lead to a better interpolation.
+ </p>
+ <p>
+ Loess (or Lowess) interpolation is a robust interpolation useful for
+ smoothing univariate scaterplots. It has been described by William
+ Cleveland in his 1979 seminal paper <a
+ href="http://www.math.tau.ac.il/~yekutiel/MA%20seminar/Cleveland%201979.pdf">Robust
+ Locally Weighted Regression and Smoothing Scatterplots</a>. This kind of
+ interpolation is computationally intensive but robust.
+ </p>
+ <p>
+ Microsphere interpolation is a robust multidimensional interpolation algorithm.
+ It has been described in William Dudziak's <a
+ href="http://www.dudziak.com/microsphere.pdf">MS thesis</a>.
+ </p>
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/analysis/interpolation/BivariateRealGridInterpolator.html">
+ BivariateRealGridInterpolator</a> is used to find a bivariate real-valued
+ function <code>f</code> which for a given set of tuples
+ (<code>x<sub>i</sub></code>,<code>y<sub>j</sub></code>,<code>f<sub>ij</sub></code>)
+ yields <code>f(x<sub>i</sub>,y<sub>j</sub>)=f<sub>ij</sub></code> to the best accuracy
+ possible. The result is provided as an object implementing the
+ <a href="../apidocs/org/apache/commons/math/analysis/BivariateRealFunction.html">
+ BivariateRealFunction</a> interface. It can therefore be evaluated at any point,
+ including a point not belonging to the original set.
+ The arrays <code>x<sub>i</sub></code> and <code>y<sub>j</sub></code> must be
+ sorted in increasing order in order to define a two-dimensional grid.
+ </p>
+ <p>
+ In <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">bicubic interpolation</a>,
+ the interpolation function is a 3rd-degree polynomial of two variables. The coefficients
+ are computed from the function values sampled on a grid, as well as the values of the
+ partial derivatives of the function at those grid points.
+ From two-dimensional data sampled on a grid, the
+ <a href="../apidocs/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolator.html">
+ BicubicSplineInterpolator</a> computes a
+ <a href="../apidocs/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunction.html">
+ bicubic interpolating function</a>.
+ Prior to computing an interpolating function, the
+ <a href="../apidocs/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.html">
+ SmoothingPolynomialBicubicSplineInterpolator</a> class performs smoothing of
+ the data by computing the polynomial that best fits each of the one-dimensional
+ curves along each of the coordinate axes.
+ </p>
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/analysis/interpolation/TrivariateRealGridInterpolator.html">
+ TrivariateRealGridInterpolator</a> is used to find a trivariate real-valued
+ function <code>f</code> which for a given set of tuples
+ (<code>x<sub>i</sub></code>,<code>y<sub>j</sub></code>,<code>z<sub>k</sub></code>,
+ <code>f<sub>ijk</sub></code>)
+ yields <code>f(x<sub>i</sub>,y<sub>j</sub>,z<sub>k</sub>)=f<sub>ijk</sub></code>
+ to the best accuracy possible. The result is provided as an object implementing the
+ <a href="../apidocs/org/apache/commons/math/analysis/TrivariateRealFunction.html">
+ TrivariateRealFunction</a> interface. It can therefore be evaluated at any point,
+ including a point not belonging to the original set.
+ The arrays <code>x<sub>i</sub></code>, <code>y<sub>j</sub></code> and
+ <code>z<sub>k</sub></code> must be sorted in increasing order in order to define
+ a three-dimensional grid.
+ </p>
+ <p>
+ In <a href="http://en.wikipedia.org/wiki/Tricubic_interpolation">tricubic interpolation</a>,
+ the interpolation function is a 3rd-degree polynomial of three variables. The coefficients
+ are computed from the function values sampled on a grid, as well as the values of the
+ partial derivatives of the function at those grid points.
+ From three-dimensional data sampled on a grid, the
+ <a href="../apidocs/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolator.html">
+ TricubicSplineInterpolator</a> computes a
+ <a href="../apidocs/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunction.html">
+ tricubic interpolating function</a>.
+ </p>
+ </subsection>
+ <subsection name="4.4 Integration" href="integration">
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/analysis/integration/UnivariateRealIntegrator.html">
+ UnivariateRealIntegrator</a> provides the means to numerically integrate
+ <a href="../apidocs/org/apache/commons/math/analysis/UnivariateRealFunction.html">
+ univariate real-valued functions</a>.
+ Commons-Math includes implementations of the following integration algorithms: <ul>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/integration/RombergIntegrator.html">
+ Romberg's method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/integration/SimpsonIntegrator.html">
+ Simpson's method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/integration/TrapezoidIntegrator.html">
+ trapezoid method</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/analysis/integration/LegendreGaussIntegrator.html">
+ Legendre-Gauss method</a></li>
+ </ul>
+ </p>
+ </subsection>
+ <subsection name="4.5 Polynomials" href="polynomials">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/analysis/polynomials/package-summary.html">
+ org.apache.commons.math.analysis.polynomials</a> package provides real
+ coefficients polynomials.
+ </p>
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/analysis/polynomials/PolynomialFunction.html">
+ PolynomialFunction</a> class is the most general one, using traditional
+ coefficients arrays. The
+ <a href="../apidocs/org/apache/commons/math/analysis/polynomials/PolynomialsUtils.html">
+ PolynomialsUtils</a> utility class provides static factory methods to build
+ Chebyshev, Hermite, Lagrange and Legendre polynomials. Coefficients are
+ computed using exact fractions so these factory methods can build polynomials
+ up to any degree.
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/complex.xml b/src/site/xdoc/userguide/complex.xml
new file mode 100644
index 0000000..72f8547
--- /dev/null
+++ b/src/site/xdoc/userguide/complex.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="stat.html">
+ <properties>
+ <title>The Commons Math User Guide - Complex Numbers</title>
+ </properties>
+ <body>
+ <section name="7 Complex Numbers">
+ <subsection name="7.1 Overview" href="overview">
+ <p>
+ The complex packages provides a complex number type as well as complex
+ versions of common transcendental functions and complex number
+ formatting.
+ </p>
+ </subsection>
+ <subsection name="7.2 Complex Numbers" href="complex">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/complex/Complex.html">
+ Complex</a> provides a complex number type that forms the basis for
+ the complex functionality found in commons-math.
+ </p>
+ <p>
+ Complex functions and arithmetic operations are implemented in
+ commons-math by applying standard computational formulas and
+ following the rules for <code>java.lang.Double</code> arithmetic in
+ handling infinite and <code>NaN</code> values. No attempt is made
+ to comply with ANSII/IEC C99x Annex G or any other standard for
+ Complex arithmetic. See the class and method javadocs for the
+ <a href="../apidocs/org/apache/commons/math/complex/Complex.html">
+ Complex</a> and
+ <a href="../apidocs/org/apache/commons/math/complex/ComplexUtils.html">
+ ComplexUtils</a> classes for details on computing formulas.
+ </p>
+ <p>
+ To create a complex number, simply call the constructor passing in two
+ floating-point arguments, the first being the real part of the
+ complex number and the second being the imaginary part:
+ <source>Complex c = new Complex(1.0, 3.0); // 1 + 3i</source>
+ </p>
+ <p>
+ Complex numbers may also be created from polar representations
+ using the <code>polar2Complex</code> method in
+ <code>ComplexUtils</code>.
+ </p>
+ <p>
+ The <code>Complex</code> class provides basic unary and binary
+ complex number operations. These operations provide the means to add,
+ subtract, multiply and divide complex numbers along with other
+ complex number functions similar to the real number functions found in
+ <code>java.math.BigDecimal</code>:
+ <source>Complex lhs = new Complex(1.0, 3.0);
+Complex rhs = new Complex(2.0, 5.0);
+
+Complex answer = lhs.add(rhs); // add two complex numbers
+ answer = lhs.subtract(rhs); // subtract two complex numbers
+ answer = lhs.abs(); // absolute value
+ answer = lhs.conjugate(rhs); // complex conjugate</source>
+ </p>
+ </subsection>
+ <subsection name="7.3 Complex Transcendental Functions" href="function">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/complex/Complex.html">
+ Complex</a> also provides implementations of serveral transcendental
+ functions involving complex number arguments.
+ Prior to version 1.2, these functions were provided
+ by <a href="../apidocs/org/apache/commons/math/complex/ComplexUtils.html">
+ ComplexUtils</a> in a way similar to the real number functions found in
+ <code>java.lang.Math</code>, but this has been deprecated.
+ These operations provide the means to compute the log, sine, tangent,
+ and other complex values :
+ <source>Complex first = new Complex(1.0, 3.0);
+Complex second = new Complex(2.0, 5.0);
+
+Complex answer = first.log(); // natural logarithm.
+ answer = first.cos(); // cosine
+ answer = first.pow(second); // first raised to the power of second</source>
+ </p>
+ </subsection>
+ <subsection name="7.4 Complex Formatting and Parsing" href="formatting">
+ <p>
+ <code>Complex</code> instances can be converted to and from strings
+ using the<a href="../apidocs/org/apache/commons/math/complex/ComplexFormat.html">
+ ComplexFormat</a> class.
+ <code>ComplexFormat</code> is a <code>java.text.Format</code>
+ extension and, as such, is used like other formatting objects (e.g.
+ <code>java.text.SimpleDateFormat</code>):
+ <source>ComplexFormat format = new ComplexFormat(); // default format
+Complex c = new Complex(1.1111, 2.2222);
+String s = format.format(c); // s contains "1.11 + 2.22i"</source>
+ </p>
+ <p>
+ To customize the formatting output, one or two
+ <code>java.text.NumberFormat</code> instances can be used to construct
+ a <code>ComplexFormat</code>. These number formats control the
+ formatting of the real and imaginary values of the complex number:
+ <source>NumberFormat nf = NumberFormat.getInstance();
+nf.setMinimumFractionDigits(3);
+nf.setMaximumFractionDigits(3);
+
+// create complex format with custom number format
+// when one number format is used, both real and
+// imaginary parts are formatted the same
+ComplexFormat cf = new ComplexFormat(nf);
+Complex c = new Complex(1.11, 2.2222);
+String s = format.format(c); // s contains "1.110 + 2.222i"
+
+NumberFormat nf2 = NumberFormat.getInstance();
+nf.setMinimumFractionDigits(1);
+nf.setMaximumFractionDigits(1);
+
+// create complex format with custom number formats
+cf = new ComplexFormat(nf, nf2);
+s = format.format(c); // s contains "1.110 + 2.2i"</source>
+ </p>
+ <p>
+ Another formatting customization provided by
+ <code>ComplexFormat</code> is the text used for the imaginary
+ designation. By default, the imaginary notation is "i" but, it can be
+ manipulated using the <code>setImaginaryCharacter</code> method.
+ </p>
+ <p>
+ Formatting inverse operation, parsing, can also be performed by
+ <code>ComplexFormat</code>. Parse a complex number from a string,
+ simply call the <code>parse</code> method:
+ <source>ComplexFormat cf = new ComplexFormat();
+Complex c = cf.parse("1.110 + 2.222i");</source>
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/distribution.xml b/src/site/xdoc/userguide/distribution.xml
new file mode 100644
index 0000000..11cfd8f
--- /dev/null
+++ b/src/site/xdoc/userguide/distribution.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="distribution.html">
+ <properties>
+ <title>The Commons Math User Guide - Distributions</title>
+ </properties>
+ <body>
+ <section name="8 Probability Distributions">
+ <subsection name="8.1 Overview" href="overview">
+ <p>
+ The distributions package provide a framework for some commonly used
+ probability distributions.
+ </p>
+ </subsection>
+ <subsection name="8.2 Distribution Framework" href="distributions">
+ <p>
+ The distribution framework provides the means to compute probability density
+ function (PDF) probabilities and cumulative distribution function (CDF)
+ probabilities for common probability distributions. Along with the direct
+ computation of PDF and CDF probabilities, the framework also allows for the
+ computation of inverse PDF and inverse CDF values.
+ </p>
+ <p>
+ Using a distribution object, PDF and CDF probabilities are easily computed
+ using the <code>cumulativeProbability</code> methods. For a distribution
+ <code>X</code>, and a domain value, <code>x</code>,
+ <code>cumulativeProbability</code> computes <code>P(X <= x)</code>
+ (i.e. the lower tail probability of <code>X</code>).
+ </p>
+<source>TDistribution t = new TDistributionImpl(29);
+double lowerTail = t.cumulativeProbability(-2.656); // P(T <= -2.656)
+double upperTail = 1.0 - t.cumulativeProbability(2.75); // P(T >= 2.75)</source>
+ <p>
+ The inverse PDF and CDF values are just as easily computed using the
+ <code>inverseCumulativeProbability</code> methods. For a distribution <code>X</code>,
+ and a probability, <code>p</code>, <code>inverseCumulativeProbability</code>
+ computes the domain value <code>x</code>, such that:
+ <ul>
+ <li><code>P(X <= x) = p</code>, for continuous distributions</li>
+ <li><code>P(X <= x) <= p</code>, for discrete distributions</li>
+ </ul>
+ Notice the different cases for continuous and discrete distributions. This is the result
+ of PDFs not being invertible functions. As such, for discrete distributions, an exact
+ domain value can not be returned. Only the "best" domain value. For Commons-Math, the "best"
+ domain value is determined by the largest domain value whose cumulative probability is
+ less-than or equal to the given probability.
+ </p>
+ </subsection>
+ <subsection name="8.3 User Defined Distributions" href="userdefined">
+ <p>
+ Since there are numerous distributions and Commons-Math only directly
+ supports a handful, it may be necessary to extend the distribution
+ framework to satisfy individual needs. It is recommended that the
+ <a href="../apidocs/org/apache/commons/math/distribution/Distribution.html">Distribution</a>,
+ <a href="../apidocs/org/apache/commons/math/distribution/ContinuousDistribution.html">
+ ContinuousDistribution</a>,
+ <a href="../apidocs/org/apache/commons/math/distribution/DiscreteDistribution.html">
+ DiscreteDistribution</a>, and <a href="../apidocs/org/apache/commons/math/distribution/IntegerDistribution.html">
+ IntegerDistribution</a> interfaces serve as
+ base types for any extension. These serve as the basis for all the
+ distributions directly supported by Commons-Math and using those interfaces
+ for implementation purposes will ensure any extension is compatible with the
+ remainder of Commons-Math. To aid in implementing a distribution extension,
+ the <a href="../apidocs/org/apache/commons/math/distribution/AbstractDistribution.html">
+ AbstractDistribution</a>, <a href="../apidocs/org/apache/commons/math/distribution/AbstractContinuousDistribution.html">
+ AbstractContinuousDistribution</a>, and <a href="../apidocs/org/apache/commons/math/distribution/AbstractIntegerDistribution.html">
+ AbstractIntegerDistribution</a> provide implementation building blocks and
+ offer basic distribution functionality. By extending these abstract classes
+ directly, much of the repetitive distribution implementation is already
+ developed and should save time and effor in developing user-defined
+ distributions.
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/fraction.xml b/src/site/xdoc/userguide/fraction.xml
new file mode 100644
index 0000000..3166800
--- /dev/null
+++ b/src/site/xdoc/userguide/fraction.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="fraction.html">
+ <properties>
+ <title>The Commons Math User Guide - Fractions</title>
+ </properties>
+ <body>
+ <section name="9 Fractions">
+ <subsection name="9.1 Overview" href="overview">
+ <p>
+ The fraction packages provides a fraction number type as well as
+ fraction number formatting.
+ </p>
+ </subsection>
+ <subsection name="9.2 Fraction Numbers" href="fraction">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/fraction/Fraction.html">
+ Fraction</a> and <a href="../apidocs/org/apache/commons/math/fraction/BigFraction.html">
+ BigFraction</a> provide fraction number type that forms the basis for
+ the fraction functionality found in Commons-Math. The former one can be
+ used for fractions whose numerators and denominators are small enough
+ to fit in an int (taking care of intermediate values) while the second
+ class should be used when there is a risk the numerator and denominator
+ grow very large.
+ </p>
+ <p>
+ A fraction number, can be built from two integer arguments representing numerator
+ and denominator or from a double which will be approximated:
+ <source>Fraction f = new Fraction(1, 3); // 1 / 3
+Fraction g = new Fraction(0.25); // 1 / 4</source>
+ </p>
+ <p>
+ Of special note with fraction construction, when a fraction is created it is always reduced to lowest terms.
+ </p>
+ <p>
+ The <code>Fraction</code> class provides many unary and binary
+ fraction operations. These operations provide the means to add,
+ subtract, multiple and, divide fractions along with other functions similar to the real number functions found in
+ <code>java.math.BigDecimal</code>:
+ <source>Fraction lhs = new Fraction(1, 3);
+Fraction rhs = new Fraction(2, 5);
+
+Fraction answer = lhs.add(rhs); // add two fractions
+ answer = lhs.subtract(rhs); // subtract two fractions
+ answer = lhs.abs(); // absolute value
+ answer = lhs.reciprocal(); // reciprocal of lhs</source>
+ </p>
+ <p>
+ Like fraction construction, for each of the fraction functions, the resulting fraction is reduced to lowest terms.
+ </p>
+ </subsection>
+ <subsection name="9.3 Fraction Formatting and Parsing" href="formatting">
+ <p>
+ <code>Fraction</code> instances can be converted to and from strings
+ using the<a href="../apidocs/org/apache/commons/math/fraction/FractionFormat.html">
+ FractionFormat</a> class. <code>FractionFormat</code> is a
+ <code>java.text.Format</code> extension and, as such, is used like other
+ formatting objects (e.g. <code>java.text.SimpleDateFormat</code>):
+ <source>FractionFormat format = new FractionFormat(); // default format
+Fraction f = new Fraction(2, 4);
+String s = format.format(f); // s contains "1 / 2", note the reduced fraction</source>
+ </p>
+ <p>
+ To customize the formatting output, one or two
+ <code>java.text.NumberFormat</code> instances can be used to construct
+ a <code>FractionFormat</code>. These number formats control the
+ formatting of the numerator and denominator of the fraction:
+ <source>NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE);
+// create fraction format with custom number format
+// when one number format is used, both numerator and
+// denominator are formatted the same
+FractionFormat format = new FractionFormat(nf);
+Fraction f = new Fraction(2000, 3333);
+String s = format.format(c); // s contains "2.000 / 3.333"
+
+NumberFormat nf2 = NumberFormat.getInstance(Locale.US);
+// create fraction format with custom number formats
+format = new FractionFormat(nf, nf2);
+s = format.format(f); // s contains "2.000 / 3,333"</source>
+ </p>
+ <p>
+ Formatting's inverse operation, parsing, can also be performed by
+ <code>FractionFormat</code>. To parse a fraction from a string,
+ simply call the <code>parse</code> method:
+ <source>FractionFormat ff = new FractionFormat();
+Fraction f = ff.parse("-10 / 21");</source>
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/genetics.xml b/src/site/xdoc/userguide/genetics.xml
new file mode 100644
index 0000000..14d6326
--- /dev/null
+++ b/src/site/xdoc/userguide/genetics.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="genetics.html">
+ <properties>
+ <title>The Commons Math User Guide - Genetic Algorithms</title>
+ </properties>
+ <body>
+ <section name="14 Genetic Algorithms">
+ <subsection name="14.1 Overview" href="overview">
+ <p>
+ The genetics package provides a framework and implementations for
+ genetic algorithms.
+ </p>
+ </subsection>
+ <subsection name="14.2 GA Framework">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/genetics/GeneticAlgorithm.html">
+ GeneticAlgorithm</a> provides an execution framework for Genetic Algorithms (GA).
+ <a href="../apidocs/org/apache/commons/math/genetics/Population.html">
+ Populations,</a> consisting of <a href="../apidocs/org/apache/commons/math/genetics/Chromosome.html">
+ Chromosomes</a> are evolved by the <code>GeneticAlgorithm</code> until a
+ <a href="../apidocs/org/apache/commons/math/genetics/StoppingCondition.html">
+ StoppingCondition</a> is reached. Evolution is determined by <a href="../apidocs/org/apache/commons/math/genetics/SelectionPolicy.html">
+ SelectionPolicy</a>, <a href="../apidocs/org/apache/commons/math/genetics/MutationPolicy.html">
+ MutationPolicy</a> and <a href="../apidocs/org/apache/commons/math/genetics/Fitness.html">
+ Fitness</a>.
+ </p>
+ <p>
+ The GA itself is implemented by the <code>evolve</code> method of the
+ <code>GeneticAlgorithm</code> class,
+ which looks like this:
+ <source>public Population evolve(Population initial, StoppingCondition condition) {
+ Population current = initial;
+ while (!condition.isSatisfied(current)) {
+ current = nextGeneration(current);
+ }
+ return current;
+}
+ </source>
+ The <code>nextGeneration</code> method implements the following algorithm:
+ <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 <code>SelectionPolicy</code> to select a pair of parents
+ from <code>current</code></li>
+ <li>With probability =
+ <a href="../apidocs/org/apache/commons/math/genetics/GeneticAlgorithm.html#getCrossoverRate()">
+ getCrossoverRate()</a>, apply configured <code>CrossoverPolicy</code> to parents</li>
+ <li>With probability =
+ <a href="../apidocs/org/apache/commons/math/genetics/GeneticAlgorithm.html#getMutationRate()">
+ getMutationRate()</a>,
+ apply configured <code>MutationPolicy</code> to each of the offspring</li>
+ <li>Add offspring individually to nextGeneration,
+ space permitting</li>
+ </ul>
+ <li>Return nextGeneration</li>
+ </ol>
+ </p>
+ </subsection>
+ <subsection name="14.3 Implementation">
+ <p>
+ Here is an example GA execution:
+ <source>
+// initialize a new genetic algorithm
+GeneticAlgorithm ga = new GeneticAlgorithm(
+ new OnePointCrossover<Integer>(),
+ 1,
+ new RandomKeyMutation(),
+ 0.10,
+ new TournamentSelection(TOURNAMENT_ARITY)
+);
+
+// initial population
+Population initial = getInitialPopulation();
+
+// stopping condition
+StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
+
+// run the algorithm
+Population finalPopulation = ga.evolve(initial, stopCond);
+
+// best chromosome from the final population
+Chromosome bestFinal = finalPopulation.getFittestChromosome();
+ </source>
+ The arguments to the <code>GeneticAlgorithm</code> constructor above are: <br/>
+ <table>
+ <tr><th>Parameter</th><th>value in example</th><th>meaning</th></tr>
+ <tr><td>crossoverPolicy</td>
+ <td><a href="../apidocs/org/apache/commons/math/genetics/OnePointCrossover.html">OnePointCrossover</a></td>
+ <td>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.</td></tr>
+ <tr><td>crossoverRate</td>
+ <td>1</td>
+ <td>Always apply crossover</td></tr>
+ <tr><td>mutationPolicy</td>
+ <td><a href="../apidocs/org/apache/commons/math/genetics/RandomKeyMutation.html">RandomKeyMutation</a></td>
+ <td>Changes a randomly chosen element of the array representation to a random value uniformly distributed in [0,1].</td></tr>
+ <tr><td>mutationRate</td>
+ <td>.1</td>
+ <td>Apply mutation with probability 0.1 - that is, 10% of the time.</td></tr>
+ <tr><td>selectionPolicy</td>
+ <td><a href="../apidocs/org/apache/commons/math/genetics/TournamentSelection.html">TournamentSelection</a></td>
+ <td>Each of the two selected chromosomes is selected based on an n-ary tournament -- this is done by drawing
+ n random chromosomes without replacement from the population, and then selecting the fittest chromosome among them.</td></tr>
+ </table><br/>
+ The algorithm starts with an <code>initial</code> population of <code>Chromosomes.</code> and executes until
+ the specified <a href="../apidocs/org/apache/commons/math/genetics/StoppingCondition.html">StoppingCondition</a>
+ is reached. In the example above, a
+ <a href="../apidocs/org/apache/commons/math/genetics/FixedGenerationCount.html">FixedGenerationCount</a>
+ stopping condition is used, which means the algorithm proceeds through a fixed number of generations.
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/geometry.xml b/src/site/xdoc/userguide/geometry.xml
new file mode 100644
index 0000000..28aa8cc
--- /dev/null
+++ b/src/site/xdoc/userguide/geometry.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="geometry.html">
+
+ <properties>
+ <title>The Commons Math User Guide - Geometry</title>
+ </properties>
+
+ <body>
+ <section name="11 Geometry">
+ <subsection name="11.1 Overview" href="overview">
+ <p>
+ The geometry package provides classes useful for many physical simulations
+ in the real 3D space, namely vectors and rotations.
+ </p>
+ </subsection>
+ <subsection name="11.2 Vectors" href="vectors">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/geometry/Vector3D.html">
+ Vector3D</a> provides a simple vector type. One important feature is
+ that instances of this class are guaranteed
+ to be immutable, this greatly simplifies modelling dynamical systems
+ with changing states: once a vector has been computed, a reference to it
+ is known to preserve its state as long as the reference itself is preserved.
+ </p>
+ <p>
+ Numerous constructors are available to create vectors. In addition to the
+ straightforward cartesian coordinates constructor, a constructor using
+ azimuthal coordinates can build normalized vectors and linear constructors
+ from one, two, three or four base vectors are also available. Constants have
+ been defined for the most commons vectors (plus and minus canonical axes,
+ null vector, and special vectors with infinite or NaN coordinates).
+ </p>
+ <p>
+ The generic vectorial space operations are available including dot product,
+ normalization, orthogonal vector finding and angular separation computation
+ which have a specific meaning in 3D. The 3D geometry specific cross product
+ is of course also implemented.
+ </p>
+ <p>
+ <a href="../apidocs/org/apache/commons/math/geometry/Vector3DFormat.html">
+ Vector3DFormat</a> is a specialized format for formatting output or parsing
+ input with text representation of 3D vectors.
+ </p>
+ </subsection>
+ <subsection name="11.3 Rotations" href="rotations">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/geometry/Rotation.html">
+ Rotation</a> represents 3D rotations.
+ Rotation instances are also immutable objects, as Vector3D instances.
+ </p>
+ <p>
+ Rotations can be represented by several different mathematical
+ entities (matrices, axe and angle, Cardan or Euler angles,
+ quaternions). This class presents a higher level abstraction, more
+ user-oriented and hiding 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 implicitely 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 can be done using the
+ following single line of code:
+ </p>
+ <source>double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ);</source>
+ <p>
+ Focus is oriented on what a rotation <em>does</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 vectors into other three
+ dimensional vectors. Depending on the application, the meaning of
+ these vectors may vary as well as the semantics of the rotation.
+ </p>
+ <p>
+ For example in a spacecraft attitude simulation tool, users will
+ often consider the vectors are fixed (say the Earth direction for
+ example) and the rotation transforms the coordinates coordinates of
+ this 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 (we have fixed vectors and moving frame).
+ 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 (we have moving vectors in fixed frame). 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.
+ </p>
+ <p>
+ These examples show that a rotation means what the user wants it to
+ mean, so 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: <code>applyTo(Vector3D)</code> and
+ <code>applyInverseTo(Vector3D)</code>.
+ </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: <code>applyTo(Rotation)</code> and
+ <code>applyInverseTo(Rotation)</code>.
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/index.xml b/src/site/xdoc/userguide/index.xml
new file mode 100644
index 0000000..da6bdf2
--- /dev/null
+++ b/src/site/xdoc/userguide/index.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 920131 $ $Date: 2010-03-07 23:19:18 +0100 (dim. 07 mars 2010) $ -->
+<document url="index.html">
+ <properties>
+ <title>The Commons Math User Guide - Table of Contents</title>
+ </properties>
+
+ <body>
+ <section name="Table of Contents" href="toc">
+
+ <ul>
+ <li><a href="overview.html">0. Overview</a>
+ <ul>
+ <li><a href="overview.html#a0.1_About_the_User_Guide">0.1 About the User Guide</a></li>
+ <li><a href="overview.html#a0.2_Whats_in_commons-math">0.2 What's in commons-math</a></li>
+ <li><a href="overview.html#a0.3_How_commons-math_is_organized">0.3 How commons-math is organized</a></li>
+ <li><a href="overview.html#a0.4_How_interface_contracts_are_specified_in_commons-math_javadoc">0.4 How interface contracts are specified in commons-math javadoc</a></li>
+ <li><a href="overview.html#a0.5_Dependencies">0.5 Dependencies</a></li>
+ </ul></li>
+
+ <li><a href="stat.html">1. Statistics</a>
+ <ul>
+ <li><a href="stat.html#a1.1_Overview">1.1 Overview</a></li>
+ <li><a href="stat.html#a1.2_Descriptive_statistics">1.2 Descriptive statistics</a></li>
+ <li><a href="stat.html#a1.3_Frequency_distributions">1.3 Frequency distributions</a></li>
+ <li><a href="stat.html#a1.4_Simple_regression">1.4 Simple regression</a></li>
+ <li><a href="stat.html#a1.5_Multiple_linear_regression">1.5 Multiple Regression</a></li>
+ <li><a href="stat.html#a1.6_Rank_transformations">1.6 Rank transformations</a></li>
+ <li><a href="stat.html#a1.7_Covariance_and_correlation">1.7 Covariance and correlation</a></li>
+ <li><a href="stat.html#a1.8_Statistical_tests">1.8 Statistical Tests</a></li>
+ </ul></li>
+ <li><a href="random.html">2. Data Generation</a>
+ <ul>
+ <li><a href="random.html#a2.1_Overview">2.1 Overview</a></li>
+ <li><a href="random.html#a2.2_Random_numbers">2.2 Random numbers</a></li>
+ <li><a href="random.html#a2.3_Random_Vectors">2.3 Random Vectors</a></li>
+ <li><a href="random.html#a2.4_Random_Strings">2.4 Random Strings</a></li>
+ <li><a href="random.html#a2.5_Random_permutations_combinations_sampling">2.5 Random permutations, combinations, sampling</a></li>
+ <li><a href="random.html#a2.6_Generating_data_like_an_input_file">2.6 Generating data 'like' an input file</a></li>
+ <li><a href="random.html#a2.7_PRNG_Pluggability">2.7 PRNG Pluggability</a></li>
+ </ul></li>
+ <li><a href="linear.html">3. Linear Algebra</a>
+ <ul>
+ <li><a href="linear.html#a3.1_Overview">3.1 Overview</a></li>
+ <li><a href="linear.html#a3.2_Real_matrices">3.2 Real matrices</a></li>
+ <li><a href="linear.html#a3.3_Real_vectors">3.3 Real vectors</a></li>
+ <li><a href="linear.html#a3.4_Solving_linear_systems">3.4 Solving linear systems</a></li>
+ <li><a href="linear.html#a3.5_Eigenvalueseigenvectors_and_singular_valuessingular_vectors">3.5 Eigenvalues/eigenvectors and singular values/singular vectors</a></li>
+ <li><a href="linear.html#a3.6_Non-real_fields_complex_fractions_...">3.6 Non-real fields (complex, fractions ...)</a></li>
+ </ul></li>
+ <li><a href="analysis.html">4. Numerical Analysis</a>
+ <ul>
+ <li><a href="analysis.html#a4.1_Overview">4.1 Overview</a></li>
+ <li><a href="analysis.html#a4.2_Root-finding">4.2 Root-finding</a></li>
+ <li><a href="analysis.html#a4.3_Interpolation">4.3 Interpolation</a></li>
+ <li><a href="analysis.html#a4.4_Integration">4.4 Integration</a></li>
+ <li><a href="analysis.html#a4.5_Polynomials">4.5 Polynomials</a></li>
+ </ul></li>
+ <li><a href="special.html">5. Special Functions</a>
+ <ul>
+ <li><a href="special.html#a5.1_Overview">5.1 Overview</a></li>
+ <li><a href="special.html#a5.2_Erf_functions">5.2 Erf functions</a></li>
+ <li><a href="special.html#a5.3_Gamma_functions">5.3 Gamma functions</a></li>
+ <li><a href="special.html#a5.4_Beta_funtions">5.4 Beta funtions</a></li>
+ </ul></li>
+ <li><a href="utilities.html">6. Utilities</a>
+ <ul>
+ <li><a href="utilities.html#a6.1_Overview">6.1 Overview</a></li>
+ <li><a href="utilities.html#a6.2_Double_array_utilities">6.2 Double array utilities</a></li>
+ <li><a href="utilities.html#a6.3_intdouble_hash_map">6.3 int/double hash map</a></li>
+ <li><a href="utilities.html#a6.4_Continued_Fractions">6.4 Continued Fractions</a></li>
+ <li><a href="utilities.html#a6.5_binomial_coefficients_factorials_and_other_common_math_functions">6.5 binomial coefficients, factorials and other common math functions</a></li>
+ </ul></li>
+ <li><a href="complex.html">7. Complex Numbers</a>
+ <ul>
+ <li><a href="complex.html#a7.1_Overview">7.1 Overview</a></li>
+ <li><a href="complex.html#a7.2_Complex_Numbers">7.2 Complex Numbers</a></li>
+ <li><a href="complex.html#a7.3_Complex_Transcendental_Functions">7.3 Complex Transcendental Functions</a></li>
+ <li><a href="complex.html#a7.4_Complex_Formatting_and_Parsing">7.4 Complex Formatting and Parsing</a></li>
+ </ul></li>
+ <li><a href="distribution.html">8. Probability Distributions</a>
+ <ul>
+ <li><a href="distribution.html#a8.1_Overview">8.1 Overview</a></li>
+ <li><a href="distribution.html#a8.2_Distribution_Framework">8.2 Distribution Framework</a></li>
+ <li><a href="distribution.html#a8.3_User_Defined_Distributions">8.3 User Defined Distributions</a></li>
+ </ul></li>
+ <li><a href="fraction.html">9. Fractions</a>
+ <ul>
+ <li><a href="fraction.html#a9.1_Overview">9.1 Overview</a></li>
+ <li><a href="fraction.html#a9.2_Fraction_Numbers">9.2 Fraction Numbers</a></li>
+ <li><a href="fraction.html#a9.3_Fraction_Formatting_and_Parsing">9.3 Fraction Formatting and Parsing</a></li>
+ </ul></li>
+ <li><a href="transform.html">10. Transform methods</a></li>
+ <li><a href="geometry.html">11. Geometry</a>
+ <ul>
+ <li><a href="geometry.html#a11.1_Overview">11.1 Overview</a></li>
+ <li><a href="geometry.html#a11.2_Vectors">11.2 Vectors</a></li>
+ <li><a href="geometry.html#a11.3_Rotations">11.3 Rotations</a></li>
+ </ul></li>
+ <li><a href="optimization.html">12. Optimization</a>
+ <ul>
+ <li><a href="optimization.html#a12.1_Overview">12.1 Overview</a></li>
+ <li><a href="analysis.html#a12.2_Univariate_Functions">12.2 Univariate Functions</a></li>
+ <li><a href="analysis.html#a12.3_Linear_Programming">12.3 Linear Programming</a></li>
+ <li><a href="optimization.html#a12.4_Direct_Methods">12.4 Direct Methods</a></li>
+ <li><a href="analysis.html#a12.5_General_Case">12.5 General Case</a></li>
+ <li><a href="analysis.html#a12.6_Curve_Fitting">12.6 Curve Fitting</a></li>
+ </ul></li>
+ <li><a href="ode.html">13. Ordinary Differential Equations Integration</a>
+ <ul>
+ <li><a href="ode.html#a13.1_Overview">13.1 Overview</a></li>
+ <li><a href="ode.html#a13.2_Continuous_Output">13.2 Continuous Output</a></li>
+ <li><a href="ode.html#a13.3_Discrete_Events_Handling">13.3 Discrete Events Handling</a></li>
+ <li><a href="ode.html#a13.4_Available_Integrators">13.4 Available Integrators</a></li>
+ <li><a href="ode.html#a13.5_Derivatives">13.5 Derivatives</a></li>
+ </ul></li>
+ <li><a href="genetics.html">14. Genetic Algorithms</a>
+ <ul>
+ <li><a href="genetics.html#a14.1_Overview">14.1 Overview</a></li>
+ <li><a href="genetics.html#a14.2_GA_Framework">14.2 GA Framework</a></li>
+ <li><a href="genetics.html#a14.3_Implementation">14.3 Implementation and Examples</a></li>
+ </ul></li>
+ </ul>
+ </section>
+
+ </body>
+
+</document>
diff --git a/src/site/xdoc/userguide/linear.xml b/src/site/xdoc/userguide/linear.xml
new file mode 100644
index 0000000..8685c39
--- /dev/null
+++ b/src/site/xdoc/userguide/linear.xml
@@ -0,0 +1,205 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 783702 $ $Date: 2009-06-11 10:54:02 +0200 (jeu. 11 juin 2009) $ -->
+<document url="linear.html">
+
+ <properties>
+ <title>The Commons Math User Guide - Linear Algebra</title>
+ </properties>
+
+ <body>
+ <section name="3 Linear Algebra">
+ <subsection name="3.1 Overview" href="overview">
+ <p>
+ Linear algebra support in commons-math provides operations on real matrices
+ (both dense and sparse matrices are supported) and vectors. It features basic
+ operations (addition, subtraction ...) and decomposition algorithms that can
+ be used to solve linear systems either in exact sense and in least squares sense.
+ </p>
+ </subsection>
+ <subsection name="3.2 Real matrices" href="real_matrices">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/linear/RealMatrix.html">
+ RealMatrix</a> interface represents a matrix with real numbers as
+ entries. The following basic matrix operations are supported:
+ <ul>
+ <li>Matrix addition, subtraction, multiplication</li>
+ <li>Scalar addition and multiplication</li>
+ <li>transpose</li>
+ <li>Norm and Trace</li>
+ <li>Operation on a vector</li>
+ </ul>
+ </p>
+ <p>
+ Example:
+ <source>
+// Create a real matrix with two rows and three columns
+double[][] matrixData = { {1d,2d,3d}, {2d,5d,3d}};
+RealMatrix m = new Array2DRowRealMatrix(matrixData);
+
+// One more with three rows, two columns
+double[][] matrixData2 = { {1d,2d}, {2d,5d}, {1d, 7d}};
+RealMatrix n = new Array2DRowRealMatrix(matrixData2);
+
+// Note: The constructor copies the input double[][] array.
+
+// Now multiply m by n
+RealMatrix p = m.multiply(n);
+System.out.println(p.getRowDimension()); // 2
+System.out.println(p.getColumnDimension()); // 2
+
+// Invert p, using LU decomposition
+RealMatrix pInverse = new LUDecompositionImpl(p).getSolver().getInverse();
+ </source>
+ </p>
+ <p>
+ The three main implementations of the interface are <a
+ href="../apidocs/org/apache/commons/math/linear/Array2DRowRealMatrix.html">
+ Array2DRowRealMatrix</a> and <a
+ href="../apidocs/org/apache/commons/math/linear/BlockRealMatrix.html">
+ BlockRealMatrix</a> for dense matrices (the second one being more suited to
+ dimensions above 50 or 100) and <a
+ href="../apidocs/org/apache/commons/math/linear/SparseRealMatrix.html">
+ SparseRealMatrix</a> for sparse matrices.
+ </p>
+ </subsection>
+ <subsection name="3.3 Real vectors" href="real_vectors">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/linear/RealVector.html">
+ RealVector</a> interface represents a vector with real numbers as
+ entries. The following basic matrix operations are supported:
+ <ul>
+ <li>Vector addition, subtraction</li>
+ <li>Element by element multiplication, division</li>
+ <li>Scalar addition, subtraction, multiplication, division and power</li>
+ <li>Mapping of mathematical functions (cos, sin ...)</li>
+ <li>Dot product, outer product</li>
+ <li>Distance and norm according to norms L1, L2 and Linf</li>
+ </ul>
+ </p>
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/linear/RealVectorFormat.html">
+ RealVectorFormat</a> class handles input/output of vectors in a customizable
+ textual format.
+ </p>
+ </subsection>
+ <subsection name="3.4 Solving linear systems" href="solve">
+ <p>
+ The <code>solve()</code> methods of the <a
+ href="../apidocs/org/apache/commons/math/linear/DecompositionSolver.html">DecompositionSolver</a>
+ interface support solving linear systems of equations of the form AX=B, either
+ in linear sense or in least square sense. A <code>RealMatrix</code> instance is
+ used to represent the coefficient matrix of the system. Solving the system is a
+ two phases process: first the coefficient matrix is decomposed in some way and
+ then a solver built from the decomposition solves the system. This allows to
+ compute the decomposition and build the solver only once if several systems have
+ to be solved with the same coefficient matrix.
+ </p>
+ <p>
+ For example, to solve the linear system
+ <pre>
+ 2x + 3y - 2z = 1
+ -x + 7y + 6x = -2
+ 4x - 3y - 5z = 1
+ </pre>
+ Start by decomposing the coefficient matrix A (in this case using LU decomposition)
+ and build a solver
+ <source>
+RealMatrix coefficients =
+ new Array2DRowRealMatrix(new double[][] { { 2, 3, -2 }, { -1, 7, 6 }, { 4, -3, -5 } },
+ false);
+DecompositionSolver solver = new LUDecompositionImpl(coefficients).getSolver();
+ </source>
+ Next create a <code>RealVector</code> array to represent the constant
+ vector B and use <code>solve(RealVector)</code> to solve the system
+ <source>
+RealVector constants = new RealVectorImpl(new double[] { 1, -2, 1 }, false);
+RealVector solution = solver.solve(constants);
+ </source>
+ The <code>solution</code> vector will contain values for x
+ (<code>solution.getEntry(0)</code>), y (<code>solution.getEntry(1)</code>),
+ and z (<code>solution.getEntry(2)</code>) that solve the system.
+ </p>
+ <p>
+ Each type of decomposition has its specific semantics and constraints on
+ the coefficient matrix as shown in the following table. For algorithms that
+ solve AX=B in least squares sense the value returned for X is such that the
+ residual AX-B has minimal norm. If an exact solution exist (i.e. if for some
+ X the residual AX-B is exactly 0), then this exact solution is also the solution
+ in least square sense. This implies that algorithms suited for least squares
+ problems can also be used to solve exact problems, but the reverse is not true.
+ </p>
+ <p>
+ <table border="1" align="center">
+ <tr BGCOLOR="#CCCCFF"><td colspan="2"><font size="+2">Decomposition algorithms</font></td></tr>
+ <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Name</td><td>coefficients matrix</td><td>problem type</td></font></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/linear/LUDecomposition.html">LU</a></td><td>square</td><td>exact solution only</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/linear/CholeskyDecomposition.html">Cholesky</a></td><td>symmetric positive definite</td><td>exact solution only</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/linear/QRDecomposition.html">QR</a></td><td>any</td><td>least squares solution</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/linear/EigenDecomposition.html">eigen decomposition</a></td><td>square</td><td>exact solution only</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/linear/SingularValueDecomposition.html">SVD</a></td><td>any</td><td>least squares solution</td></tr>
+ </table>
+ </p>
+ <p>
+ It is possible to use a simple array of double instead of a <code>RealVector</code>.
+ In this case, the solution will be provided also as an array of double.
+ </p>
+ <p>
+ It is possible to solve multiple systems with the same coefficient matrix
+ in one method call. To do this, create a matrix whose column vectors correspond
+ to the constant vectors for the systems to be solved and use <code>solve(RealMatrix),</code>
+ which returns a matrix with column vectors representing the solutions.
+ </p>
+ </subsection>
+ <subsection name="3.5 Eigenvalues/eigenvectors and singular values/singular vectors" href="eigen">
+ <p>
+ Decomposition algorithms may be used for themselves and not only for linear system solving.
+ This is of prime interest with eigen decomposition and singular value decomposition.
+ </p>
+ <p>
+ The <code>getEigenvalue()</code>, <code>getEigenvalues()</code>, <code>getEigenVector()</code>,
+ <code>getV()</code>, <code>getD()</code> and <code>getVT()</code> methods of the
+ <code>EigenDecomposition</code> interface support solving eigenproblems of the form
+ AX = lambda X where lambda is a real scalar.
+ </p>
+ <p>The <code>getSingularValues()</code>, <code>getU()</code>, <code>getS()</code> and
+ <code>getV()</code> methods of the <code>SingularValueDecomposition</code> interface
+ allow to solve singular values problems of the form AXi = lambda Yi where lambda is a
+ real scalar, and where the Xi and Yi vectors form orthogonal bases of their respective
+ vector spaces (which may have different dimensions).
+ </p>
+ </subsection>
+ <subsection name="3.6 Non-real fields (complex, fractions ...)" href="field">
+ <p>
+ In addition to the real field, matrices and vectors using non-real <a
+ href="../apidocs/org/apache/commons/math/FieldElement.html">field elements</a> can be used.
+ The fields already supported by the library are:
+ <ul>
+ <li><a href="../apidocs/org/apache/commons/math/complex/Complex.html">Complex</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/fraction/Fraction.html">Fraction</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/fraction/BigFraction.html">BigFraction</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/util/BigReal.html">BigReal</a></li>
+ </ul>
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/ode.xml b/src/site/xdoc/userguide/ode.xml
new file mode 100644
index 0000000..71caf7b
--- /dev/null
+++ b/src/site/xdoc/userguide/ode.xml
@@ -0,0 +1,440 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $ -->
+<document url="ode.html">
+
+ <properties>
+ <title>The Commons Math User Guide - Ordinary Differential Equations Integration</title>
+ </properties>
+
+ <body>
+ <section name="13 Ordinary Differential Equations Integration">
+ <subsection name="13.1 Overview" href="overview">
+ <p>
+ The ode package provides classes to solve Ordinary Differential Equations problems.
+ </p>
+ <p>
+ This package solves Initial Value Problems of the form y'=f(t,y) with t<sub>0</sub>
+ and y(t<sub>0</sub>)=y<sub>0</sub> known. The provided integrators compute an estimate
+ of y(t) from t=t<sub>0</sub> to t=t<sub>1</sub>.
+ </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 both the state and its derivative
+ between the time steps. They do so through classes extending the
+ <a href="../apidocs/org/apache/commons/math/ode/sampling/StepInterpolator.html">StepInterpolator</a>
+ 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
+ (occurring when the sign of user-supplied <i>switching function</i> changes). 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>
+ All integrators support setting a maximal number of evaluations of differential
+ equations function. If this number is exceeded, an exception will be thrown during
+ integration. This can be used to prevent infinite loops if for example error control or
+ discrete events create a really large number of extremely small steps. By default, the
+ maximal number of evaluation is set to <code>Integer.MAX_VALUE</code> (i.e. 2<sup>31</sup>-1
+ or 2147483647). It is recommended to set this maximal number to a value suited to the ODE
+ problem, integration range, and step size or error control settings.
+ </p>
+ <p>
+ The user should describe his problem in his own classes which should implement the
+ <a href="../apidocs/org/apache/commons/math/ode/FirstOrderDifferentialEquations.html">FirstOrderDifferentialEquations</a>
+ interface. Then he should pass it to the integrator he prefers among all the classes that implement
+ the <a href="../apidocs/org/apache/commons/math/ode/FirstOrderIntegrator.html">FirstOrderIntegrator</a>
+ interface. The following example shows how to implement the simple two-dimensional problem:
+ <ul>
+ <li>y'<sub>0</sub>(t) = ω × (c<sub>1</sub> - y<sub>1</sub>(t))</li>
+ <li>y'<sub>1</sub>(t) = ω × (y<sub>0</sub>(t) - c<sub>0</sub>)</li>
+ </ul>
+ with some initial state y(t<sub>0</sub>) = (y<sub>0</sub>(t<sub>0</sub>), y<sub>1</sub>(t<sub>0</sub>)).
+ In fact, the exact solution of this problem is that y(t) moves along a circle
+ centered at c = (c<sub>0</sub>, c<sub>1</sub>) with constant angular rate ω.
+ </p>
+ <source>
+private static class CircleODE implements FirstOrderDifferentialEquations {
+
+ private double[] c;
+ private double omega;
+
+ public CircleODE(double[] c, double omega) {
+ this.c = c;
+ this.omega = omega;
+ }
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = omega * (c[1] - y[1]);
+ yDot[1] = omega * (y[0] - c[0]);
+ }
+
+}
+ </source>
+ <p>
+ Computing the state y(16.0) starting from y(0.0) = (0.0, 1.0) and integrating the ODE
+ is done as follows (using Dormand-Prince 8(5,3) integrator as an example):
+ </p>
+ <source>
+FirstOrderIntegrator dp853 = new DormandPrince853Integrator(1.0e-8, 100.0, 1.0e-10, 1.0e-10);
+FirstOrderDifferentialEquations ode = new CircleODE(new double[] { 1.0, 1.0 }, 0.1);
+double[] y = new double[] { 0.0, 1.0 }; // initial state
+dp853.integrate(ode, 0.0, y, 16.0, y); // now y contains final state at time t=16.0
+ </source>
+ </subsection>
+ <subsection name="13.2 Continuous Output" href="continuous">
+ <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 y array of the
+ <code>FirstOrderIntegrator.integrate</code> method, as shown by previous example. 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
+ <a href="../apidocs/org/apache/commons/math/ode/sampling/StepHandler.html">StepHandler</a> interface or a
+ <a href="../apidocs/org/apache/commons/math/ode/sampling/StepNormalizer.html">StepNormalizer</a> object wrapping
+ a user-specified object implementing the
+ <a href="../apidocs/org/apache/commons/math/ode/sampling/FixedStepHandler.html">FixedStepHandler</a> interface
+ into the integrator before calling the <code>FirstOrderIntegrator.integrate</code> 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. Considering again the previous example, we want to print the
+ trajectory of the point to check it really is a circle arc. We simply add the following before the call
+ to integrator.integrate:
+ </p>
+ <source>
+StepHandler stepHandler = new StepHandler() {
+ public void reset() {}
+
+ public boolean requiresDenseOutput() { return false; }
+
+ public void handleStep(StepInterpolator interpolator, boolean isLast) throws MathUserException {
+ double t = interpolator.getCurrentTime();
+ double[] y = interpolator.getInterpolatedState();
+ System.out.println(t + " " + y[0] + " " + y[1]);
+ }
+};
+integrator.addStepHandler(stepHandler);
+ </source>
+ <p>
+ <a href="../apidocs/org/apache/commons/math/ode/ContinuousOutputModel.html">ContinuousOutputModel</a>
+ 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 file system 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 <a href="../apidocs/org/apache/commons/math/ode/sampling/StepHandler.html">StepHandler</a>
+ interface are available for general needs
+ (<a href="../apidocs/org/apache/commons/math/ode/sampling/DummyStepHandler.html">DummyStepHandler</a>,
+ <a href="../apidocs/org/apache/commons/math/ode/sampling/StepNormalizer.html">StepNormalizer</a>) 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
+ <a href="../apidocs/org/apache/commons/math/ode/AdaptiveStepsizeIntegrator.html">AdaptiveStepsizeIntegrator</a>
+ abstract class). In this case, the step handler which is called after each successful step shows up
+ the variable stepsize. The <a href="../apidocs/org/apache/commons/math/ode/sampling/StepNormalizer.html">StepNormalizer</a>
+ class can be used to convert the variable stepsize into a fixed stepsize that can be handled by classes
+ implementing the <a href="../apidocs/org/apache/commons/math/ode/sampling/FixedStepHandler.html">FixedStepHandler</a>
+ 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>
+ </subsection>
+ <subsection name="13.3 Discrete Events Handling" href="events">
+ <p>
+ ODE problems are continuous ones. However, sometimes discrete events must be
+ taken into account. The most frequent case is the stop condition of the integrator
+ is not defined by the time t but by a target condition on state y (say y[0] = 1.0
+ for example).
+ </p>
+ <p>
+ Discrete events detection is based on switching functions. The user provides
+ a simple <a href="../apidocs/org/apache/commons/math/ode/events/EventHandler.html">g(t, y)</a>
+ 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). Note that
+ g function signs changes at the very beginning of the integration (from t<sub>0</sub>
+ to t<sub>0</sub> + ε where ε is the events detection convergence threshold)
+ are explicitely ignored. This prevents having the integration stuck at its
+ initial point when a new integration is restarted just at the same point a
+ previous one had been stopped by an event.
+ </p>
+ <p>
+ When an event is triggered, the event time, current state and an indicator
+ whether the switching function was increasing or decreasing at event time
+ are provided to the user. Several different options are available to him:
+ </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:
+ </p>
+ <source>
+public double g(double t, double[] y) {
+ return y[0] - targetConcentration;
+}
+
+public int eventOccurred(double t, double[] y, boolean increasing) {
+ return STOP;
+}
+ </source>
+
+ <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 maneuvers 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:
+ </p>
+ <source>
+public double g(double t, double[] y) {
+ return (t - tManeuverStart) * (t - tManeuverStop);
+}
+
+public int eventOccurred(double t, double[] y, boolean increasing) {
+ return RESET_DERIVATIVES;
+}
+ </source>
+
+ <p>
+ The third case is useful mainly for monitoring purposes, a simple example is:
+ </p>
+ <source>
+public double g(double t, double[] y) {
+ return y[0] - y[1];
+}
+
+public int eventOccurred(double t, double[] y, boolean increasing) {
+ logger.log("y0(t) and y1(t) curves cross at t = " + t);
+ return CONTINUE;
+}
+ </source>
+ </subsection>
+ <subsection name="13.4 Available Integrators" href="integrators">
+ <p>
+ The tables below show the various integrators available for non-stiff problems. Note that the
+ implementations of Adams-Bashforth and Adams-Moulton are adaptive stepsize, not fixed stepsize
+ as is usual for these multi-step integrators. This is due to the fact the implementation relies
+ on the Nordsieck vector representation of the state.
+ </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><a href="../apidocs/org/apache/commons/math/ode/nonstiff/EulerIntegrator.html">Euler</a></td><td>1</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/MidpointIntegrator.html">Midpoint</a></td><td>2</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegrator.html">Classical Runge-Kutta</a></td><td>4</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/GillIntegrator.html">Gill</a></td><td>4</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegrator.html">3/8</a></td><td>4</td></tr>
+ </table>
+ </p>
+ <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><a href="../apidocs/org/apache/commons/math/ode/nonstiff/HighamHall54Integrator.html">Higham and Hall</a></td><td>5</td><td>4</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/DormandPrince54Integrator.html">Dormand-Prince 5(4)</a></td><td>5</td><td>4</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/DormandPrince853Integrator.html">Dormand-Prince 8(5,3)</a></td><td>8</td><td>5 and 3</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegrator.html">Gragg-Bulirsch-Stoer</a></td><td>variable (up to 18 by default)</td><td>variable</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.html">Adams-Bashforth</a></td><td>variable</td><td>variable</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.html">Adams-Moulton</a></td><td>variable</td><td>variable</td></tr>
+ </table>
+ </p>
+ </subsection>
+ <subsection name="13.5 Derivatives" href="derivatives">
+ <p>
+ If in addition to state y(t) the user needs to compute the sensitivity of the state to
+ the initial state or some parameter of the ODE, he will use the classes and interfaces
+ from the <a
+ href="../apidocs/org/apache/commons/math/ode/jacobians/package-summary.html">org.apache.commons.ode.jacobians</a>
+ package instead of the top level ode package. These classes compute the jacobians
+ dy(t)/dy<sub>0</sub> and dy(t)/dp where y<sub>0</sub> is the initial state
+ and p is some ODE parameter.
+ </p>
+ <p>
+ The classes and interfaces in this package mimic the behavior of the classes and
+ interfaces of the top level ode package, only adding parameters arrays for the jacobians.
+ The behavior of these classes is to create a compound state vector z containing both
+ the state y(t) and its derivatives dy(t)/dy<sub>0</sub> and dy(t)/dp and
+ to set up an extended problem by adding the equations for the jacobians automatically.
+ These extended state and problems are then provided to a classical underlying integrator
+ chosen by user.
+ </p>
+ <p>
+ This behavior imply there will be a top level integrator knowing about state and jacobians
+ and a low level integrator knowing only about compound state (which may be big). If the user
+ wants to deal with the top level only, he will use the specialized step handler and event
+ handler classes registered at top level. He can also register classical step handlers and
+ event handlers, but in this case will see the big compound state. This 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 will be the jacobian with respect to parameters. Dealing with low level
+ step handlers and 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.
+ </p>
+ <p>
+ In order to compute dy(t)/dy<sub>0</sub> and dy(t/dp for any t, the algorithm
+ needs not only the ODE function f such that y'=f(t,y) but also its local jacobians
+ df(t, y, p)/dy and df(t, y, p)/dp.
+ </p>
+ <p>
+ If the function f is too complex, the user can simply rely on internal differentiation
+ using finite differences to compute these local jacobians. So rather than the <a
+ href="../apidocs/org/apache/commons/math/ode/FirstOrderDifferentialEquations.html">FirstOrderDifferentialEquations</a>
+ interface he will implement the <a
+ href="../apidocs/org/apache/commons/math/ode/jacobians/ParameterizedODE.html">ParameterizedODE</a>
+ interface. Considering again our example where only ω is considered a parameter, we get:
+ </p>
+ <source>
+public class BasicCircleODE implements ParameterizedODE {
+
+ private double[] c;
+ private double omega;
+
+ public BasicCircleODE(double[] c, double omega) {
+ this.c = c;
+ this.omega = omega;
+ }
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = omega * (c[1] - y[1]);
+ yDot[1] = omega * (y[0] - c[0]);
+ }
+
+ public int getParametersDimension() {
+ // we are only interested in the omega parameter
+ return 1;
+ }
+
+ public void setParameter(int i, double value) {
+ omega = value;
+ }
+
+}
+ </source>
+ <p>
+ This ODE is provided to the specialized integrator with two arrays specifying the
+ step sizes to use for finite differences (one array for derivation with respect to
+ state y, one array for derivation with respect to parameters p):
+ </p>
+ <source>
+double[] hY = new double[] { 0.001, 0.001 };
+double[] hP = new double[] { 1.0e-6 };
+FirstOrderIntegratorWithJacobians integrator = new FirstOrderIntegratorWithJacobians(dp853, ode, hY, hP);
+integrator.integrate(t0, y0, dy0dp, t, y, dydy0, dydp);
+ </source>
+ <p>
+ If the function f is simple, the user can simply provide the local jacobians
+ by himself. So rather than the <a
+ href="../apidocs/org/apache/commons/math/ode/FirstOrderDifferentialEquations.html">FirstOrderDifferentialEquations</a>
+ interface he will implement the <a
+ href="../apidocs/org/apache/commons/math/ode/jacobians/ODEWithJacobians.html">ODEWithJacobians</a>
+ interface. Considering again our example where only ω is considered a parameter, we get:
+ </p>
+ <source>
+public class EnhancedCircleODE implements ODEWithJacobians {
+
+ private double[] c;
+ private double omega;
+
+ public EnhancedCircleODE(double[] c, double omega) {
+ this.c = c;
+ this.omega = omega;
+ }
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = omega * (c[1] - y[1]);
+ yDot[1] = omega * (y[0] - c[0]);
+ }
+
+ public int getParametersDimension() {
+ // we are only interested in the omega parameter
+ return 1;
+ }
+
+ public void computeJacobians(double t, double[] y, double[] yDot, double[][] dFdY, double[][] dFdP) {
+
+ dFdY[0][0] = 0;
+ dFdY[0][1] = -omega;
+ dFdY[1][0] = omega;
+ dFdY[1][1] = 0;
+
+ dFdP[0][0] = 0;
+ dFdP[0][1] = omega;
+ dFdP[0][2] = c[1] - y[1];
+ dFdP[1][0] = -omega;
+ dFdP[1][1] = 0;
+ dFdP[1][2] = y[0] - c[0];
+
+ }
+
+}
+ </source>
+ <p>
+ This ODE is provided to the specialized integrator as is:
+ </p>
+ <source>
+FirstOrderIntegratorWithJacobians integrator = new FirstOrderIntegratorWithJacobians(dp853, ode);
+integrator.integrate(t0, y0, dy0dp, t, y, dydy0, dydp);
+ </source>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/optimization.xml b/src/site/xdoc/userguide/optimization.xml
new file mode 100644
index 0000000..66f3888
--- /dev/null
+++ b/src/site/xdoc/userguide/optimization.xml
@@ -0,0 +1,271 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 980039 $ $Date: 2010-07-28 14:48:48 +0200 (mer. 28 juil. 2010) $ -->
+<document url="optimization.html">
+
+ <properties>
+ <title>The Commons Math User Guide - Optimization</title>
+ </properties>
+
+ <body>
+ <section name="12 Optimization">
+ <subsection name="12.1 Overview" href="overview">
+ <p>
+ The optimization package provides algorithms to optimize (i.e. either minimize
+ or maximize) some objective or cost function. The package is split in several
+ sub-packages dedicated to different kind of functions or algorithms.
+ <ul>
+ <li>the univariate package handles univariate scalar functions,</li>
+ <li>the linear package handles multivariate vector linear functions
+ with linear constraints,</li>
+ <li>the direct package handles multivariate scalar functions
+ using direct search methods (i.e. not using derivatives),</li>
+ <li>the general package handles multivariate scalar or vector functions
+ using derivatives.</li>
+ <li>the fitting package handles curve fitting by univariate real functions</li>
+ </ul>
+ </p>
+ <p>
+ The top level optimization package provides common interfaces for the optimization
+ algorithms provided in sub-packages. The main interfaces defines 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>
+ The type of goal, i.e. minimization or maximization, is defined by the enumerated
+ <a href="../apidocs/org/apache/commons/math/optimization/GoalType.html">
+ GoalType</a> which has only two values: <code>MAXIMIZE</code> and <code>MINIMIZE</code>.
+ </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><a href="../apidocs/org/apache/commons/math/optimization/UnivariateRealOptimizer.html">
+ UnivariateRealOptimizer</a> for <a
+ href="../apidocs/org/apache/commons/math/analysis/UnivariateRealFunction.html">
+ univariate real functions</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/optimization/MultivariateRealOptimizer.html">
+ MultivariateRealOptimizer</a> for <a
+ href="../apidocs/org/apache/commons/math/analysis/MultivariateRealFunction.html">
+ multivariate real functions</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/optimization/DifferentiableMultivariateRealOptimizer.html">
+ DifferentiableMultivariateRealOptimizer</a> for <a
+ href="../apidocs/org/apache/commons/math/analysis/DifferentiableMultivariateRealFunction.html">
+ differentiable multivariate real functions</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/optimization/DifferentiableMultivariateVectorialOptimizer.html">
+ DifferentiableMultivariateVectorialOptimizer</a> for <a
+ href="../apidocs/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.html">
+ differentiable multivariate vectorial functions</a></li>
+ </ul>
+ </p>
+
+ <p>
+ Despite there are only four types of supported optimizers, it is possible to optimize
+ a transform a <a
+ href="../apidocs/org/apache/commons/math/analysis/MultivariateVectorialFunction.html">
+ non-differentiable multivariate vectorial function</a> by converting it to a <a
+ href="../apidocs/org/apache/commons/math/analysis/MultivariateRealFunction.html">
+ non-differentiable multivariate real function</a> thanks to the <a
+ href="../apidocs/org/apache/commons/math/optimization/LeastSquaresConverter.html">
+ LeastSquaresConverter</a> helper class. The transformed function can be optimized using
+ any implementation of the <a
+ href="../apidocs/org/apache/commons/math/optimization/MultivariateRealOptimizer.html">
+ MultivariateRealOptimizer</a> 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>
+ </subsection>
+ <subsection name="12.2 Univariate Functions" href="univariate">
+ <p>
+ A <a href="../apidocs/org/apache/commons/math/optimization/UnivariateRealOptimizer.html">
+ UnivariateRealOptimizer</a> is used to find the minimal values of a univariate real-valued
+ function <code>f</code>.
+ </p>
+ <p>
+ These algorithms usage is very similar to root-finding algorithms usage explained
+ in the analysis package. The main difference is that the <code>solve</code> methods in root
+ finding algorithms is replaced by <code>optimize</code> methods.
+ </p>
+ </subsection>
+ <subsection name="12.3 Linear Programming" href="linear">
+ <p>
+ This package provides an implementation of George Dantzig's simplex algorithm
+ for solving linear optimization problems with linear equality and inequality
+ constraints.
+ </p>
+ </subsection>
+ <subsection name="12.4 Direct Methods" href="direct">
+ <p>
+ Direct search methods only use cost 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 cost 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 instances can be built either in single-start or in
+ multi-start mode. Multi-start is a traditional way to try to avoid
+ being trapped in a local minimum and miss the global minimum of a
+ function. It can also be used to verify the convergence of an
+ algorithm. In multi-start mode, the <code>minimizes</code>method
+ returns the best minimum found after all starts, and the <code>etMinima</code>
+ method can be used to retrieve all minima from all starts (including the one
+ already provided by the <code>minimizes</code> method).
+ </p>
+ <p>
+ The <code>direct</code> package provides two solvers. The first one is the classical
+ <a href="../apidocs/org/apache/commons/math/optimization/direct/NelderMead.html">
+ Nelder-Mead</a> method. The second one is Virginia Torczon's
+ <a href="../apidocs/org/apache/commons/math/optimization/direct/MultiDirectional.html">
+ multi-directional</a> method.
+ </p>
+ </subsection>
+ <subsection name="12.5 General Case" href="general">
+ <p>
+ The general package deals with non-linear vectorial optimization problems when
+ the partial derivatives of the objective function are available.
+ </p>
+ <p>
+ One important class of estimation problems is weighted least
+ squares problems. They basically consist in finding the values
+ for some parameters p<sub>k</sub> such that a cost function
+ J = sum(w<sub>i</sub>(mes<sub>i</sub> - mod<sub>i</sub>)<sup>2</sup>) is
+ minimized. The various (target<sub>i</sub> - model<sub>i</sub>(p<sub>k</sub>))
+ terms are called residuals. They represent the deviation between a set of
+ target values target<sub>i</sub> and theoretical values computed from
+ models model<sub>i</sub> depending on free parameters p<sub>k</sub>.
+ The w<sub>i</sub> factors are weights. One classical use case is when the
+ target values are experimental observations or measurements.
+ </p>
+ <p>
+ Solving a least-squares problem is finding the free parameters p<sub>k</sub>
+ of the theoretical models such that they are close to the target values, i.e.
+ when the residual are small.
+ </p>
+ <p>
+ Two optimizers are available in the general package, both devoted to least-squares
+ problems. The first one is based on the <a
+ href="../apidocs/org/apache/commons/math/optimization/general/GaussNewtonOptimizer.html">
+ Gauss-Newton</a> method. The second one is the <a
+ href="../apidocs/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.html">
+ Levenberg-Marquardt</a> method.
+ </p>
+ <p>
+ In order to solve a vectorial optimization problem, the user must provide it as
+ an object implementing the <a
+ href="../apidocs/org/apache/commons/math/analysis/DifferentiableMultivariateVectorialFunction.html">
+ DifferentiableMultivariateVectorialFunction</a> interface. The object will be provided to
+ the <code>estimate</code> method of the optimizer, along with the target and weight arrays,
+ thus allowing the optimizer to compute the residuals at will. The last parameter to the
+ <code>estimate</code> method is the point from which the optimizer will start its
+ search for the optimal point.
+ </p>
+ <p>
+ In addition to least squares solving, the <a
+ href="../apidocs/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizer.html">
+ NonLinearConjugateGradientOptimizer</a> class provides a non-linear conjugate gradient algorithm
+ to optimize <a
+ href="../apidocs/org/apache/commons/math/optimization/DifferentiableMultivariateRealFunction.html">
+ DifferentiableMultivariateRealFunction</a>. Both the Fletcher-Reeves and the Polak-Ribière
+ search direction update methods are supported. It is also possible to set up a preconditioner
+ or to change the line-search algorithm of the inner loop if desired (the default one is a Brent
+ solver).
+ </p>
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/optimization/general/PowellOptimizer.html">
+ PowellOptimizer</a> provides an optimization method for non-differentiable functions.
+ </p>
+ </subsection>
+ <subsection name="12.6 Curve Fitting" href="fitting">
+ <p>
+ The fitting package deals with curve fitting for univariate real functions.
+ 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>, curve fitting 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>
+ <p>
+ For all provided curve fitters, the operating principle is the same. Users must first
+ create an instance of the fitter, then add the observed points and once the complete
+ sample of observed points has been added they must call the <code>fit</code> method
+ which will compute the parameters that best fit the sample. A weight is associated
+ with each observed point, this allows to take into account uncertainty on some points
+ when they come from loosy measurements for example. If no such information exist and
+ all points should be treated the same, it is safe to put 1.0 as the weight for all points.
+ </p>
+ <p>
+ The <a
+ href="../apidocs/org/apache/commons/math/optimization/fitting/CurveFitter.html">
+ CurveFitter</a> class provides curve fitting for general curves. Users must
+ provide their own implementation of the curve template as a class implementing
+ the <a
+ href="../apidocs/org/apache/commons/math/optimization/fitting/ParametricRealFunction.html">
+ ParametricRealFunction</a> interface and they must provide the initial guess of the
+ parameters. The more specialized <a
+ href="../apidocs/org/apache/commons/math/optimization/fitting/PolynomialFitter.html">
+ PolynomialFitter</a> and <a
+ href="../apidocs/org/apache/commons/math/optimization/fitting/HarmonicFitter.html">
+ HarmonicFitter</a> classes require neither an implementation of the parametric real function
+ not an initial guess as they are able to compute them by themselves.
+ </p>
+ <p>
+ An example of fitting a polynomial is given here:
+ </p>
+ <source>PolynomialFitter fitter = new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
+fitter.addObservedPoint(-1.00, 2.021170021833143);
+fitter.addObservedPoint(-0.99 2.221135431136975);
+fitter.addObservedPoint(-0.98 2.09985277659314);
+fitter.addObservedPoint(-0.97 2.0211192647627025);
+// lots of lines ommitted
+fitter.addObservedPoint( 0.99, -2.4345814727089854);
+PolynomialFunction fitted = fitter.fit();
+ </source>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/overview.xml b/src/site/xdoc/userguide/overview.xml
new file mode 100644
index 0000000..e3fc0d4
--- /dev/null
+++ b/src/site/xdoc/userguide/overview.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 982950 $ $Date: 2010-08-06 15:29:01 +0200 (ven. 06 août 2010) $ -->
+<document>
+ <properties>
+ <title>User Guide - Overview</title>
+ </properties>
+
+<body>
+
+<section name="Overview">
+
+<subsection name="0.1 About The User Guide" href="about">
+ <p>
+ This guide is intended to help programmers quickly find what they need to develop
+ solutions using Commons Math. It also provides a supplement to the javadoc API documentation,
+ providing a little more explanation of the mathematical objects and functions included
+ in the package.
+ </p>
+</subsection>
+
+<subsection name="0.2 What's in commons-math" href="summary">
+ <p>
+ Commons Math is made up of a small set of math/stat utilities addressing
+ programming problems like the ones in the list below. This list is not exhaustive,
+ it's just meant to give a feel for the kinds of things that Commons Math provides.
+ <ul>
+ <li>Computing means, variances and other summary statistics for a list of numbers</li>
+ <li>Fitting a line to a set of data points using linear regression</li>
+ <li>Finding a smooth curve that passes through a collection of points (interpolation)</li>
+ <li>Fitting a parametric model to a set of measurements using least-squares methods</li>
+ <li>Solving equations involving real-valued functions (i.e. root-finding)</li>
+ <li>Solving systems of linear equations</li>
+ <li>Solving Ordinary Differential Equations</li>
+ <li>Minimizing multi-dimensional functions</li>
+ <li>Generating random numbers with more restrictions (e.g distribution, range) than what
+ is possible using the JDK</li>
+ <li>Generating random samples and/or datasets that are "like" the data in an input file</li>
+ <li>Performing statistical significance tests</li>
+ <li>Miscellaneous mathematical functions such as factorials, binomial
+ coefficients and "special functions" (e.g. gamma, beta functions)</li>
+ </ul></p>
+ <p>
+ We are actively seeking ideas for additional components that fit into the
+ <a href="../index.html#summary">Commons Math vision</a> of a set of lightweight,
+ self-contained math/stat components useful for solving common programming problems.
+ Suggestions for new components or enhancements to existing functionality are always welcome!
+ All feedback/suggestions for improvement should be sent to the
+ <a href="http://commons.apache.org/mail-lists.html">commons-dev mailing list</a> with
+ [math] at the beginning of the subject line.
+ </p>
+</subsection>
+
+<subsection name="0.3 How commons-math is organized" href="organization">
+ <p>
+ Commons Math is divided into fourteen subpackages, based on functionality provided.
+ <ol>
+ <li><a href="stat.html">org.apache.commons.math.stat</a> - statistics, statistical tests</li>
+ <li><a href="analysis.html">org.apache.commons.math.analysis</a> - rootfinding, integration, interpolation, polynomials</li>
+ <li><a href="random.html">org.apache.commons.math.random</a> - random numbers, strings and data generation</li>
+ <li><a href="special.html">org.apache.commons.math.special</a> - special functions (Gamma, Beta) </li>
+ <li><a href="linear.html">org.apache.commons.math.linear</a> - matrices, solving linear systems </li>
+ <li><a href="utilities.html">org.apache.commons.math.util</a> - common math/stat functions extending java.lang.Math </li>
+ <li><a href="complex.html">org.apache.commons.math.complex</a> - complex numbers</li>
+ <li><a href="distribution.html">org.apache.commons.math.distribution</a> - probability distributions</li>
+ <li><a href="fraction.html">org.apache.commons.math.fraction</a> - rational numbers</li>
+ <li><a href="transform.html">org.apache.commons.math.transform</a> - transform methods (Fast Fourier)</li>
+ <li><a href="geometry.html">org.apache.commons.math.geometry</a> - 3D geometry (vectors and rotations)</li>
+ <li><a href="optimization.html">org.apache.commons.math.optimization</a> - function maximization or minimization</li>
+ <li><a href="ode.html">org.apache.commons.math.ode</a> - Ordinary Differential Equations integration</li>
+ <li><a href="genetics.html">org.apache.commons.math.genetics</a> - Genetic Algorithms</li>
+ </ol>
+ Package javadocs are <a href="../apidocs/index.html">here</a>
+ </p>
+</subsection>
+
+<subsection name="0.4 How interface contracts are specified in commons-math javadoc" href="contracts">
+ <p>
+ You should always read the javadoc class and method comments carefully when using
+ Commons Math components in your programs. The javadoc provides references to the algorithms
+ that are used, usage notes about limitations, performance, etc. as well as interface contracts.
+ Interface contracts are specified in terms of preconditions (what has to be true in order
+ for the method to return valid results), special values returned (e.g. Double.NaN)
+ or exceptions that may be thrown if the preconditions are not met, and definitions for returned
+ values/objects or state changes.</p>
+ <p>
+ When the actual parameters provided to a method or the internal state of an object
+ make a computation meaningless, an IllegalArgumentException or IllegalStateException may
+ be thrown. Exact conditions under which runtime exceptions (and any other exceptions) are
+ thrown are specified in the javadoc method comments. In some cases, to be consistent with
+ the <a href="http://grouper.ieee.org/groups/754/">IEEE 754 standard</a> for floating point
+ arithmetic and with java.lang.Math, Commons Math methods return Double.NaN values.
+ Conditions under which Double.NaN or other special values are returned are fully specified
+ in the javadoc method comments.
+ </p>
+ <p>
+ As of version 2.2, the policy for dealing with null references is as
+ follows: When an argument is unexpectedly null, a
+ <a href="../apidocs/org/apache/commons/math/exception/NullArgumentException.html">
+ NullArgumentException</a> is raised for signalling the
+ illegal argument. Note that this class does not inherit from the
+ standard <code>NullPointerException</code> but is a subclass of
+ <code>IllegalArgumentException</code>.
+ No <code>NullPointerException</code> should be propagated from within
+ Commons-Math.
+ </p>
+</subsection>
+
+<subsection name="0.5 Dependencies" href="dependencies">
+ <p>
+ Commons Math requires JDK 1.5+ and has no runtime dependencies.
+ </p>
+</subsection>
+
+<subsection name="0.6 License" href="license">
+ <p>
+ Commons Math is distributed under the terms of the Apache License, Version 2.0:
+ <a href="http://www.apache.org/licenses/LICENSE-2.0"/>.
+ </p>
+
+ <p>
+ This product includes software developed by the University of Chicago, as Operator
+ of Argonne National Laboratory. This corresponds to software translated from the lmder,
+ lmpar and qrsolv Fortran routines from the Minpack package and distributed under the
+ following disclaimer: <a href="http://www.netlib.org/minpack/disclaimer"/>.
+ </p>
+
+ <p>
+ This product includes software translated from the odex Fortran routine developed by
+ E. Hairer and G. Wanner and distributed under the following license:
+ <a href="http://www.unige.ch/~hairer/prog/licence.txt"/>.
+ </p>
+
+ <p>
+ This product includes software translated from some LAPACK Fortran routines and
+ distributed under the following license: <a href="http://www.netlib.org/lapack/LICENSE"/>.
+ </p>
+
+</subsection>
+
+</section>
+
+</body>
+</document>
+
diff --git a/src/site/xdoc/userguide/random.xml b/src/site/xdoc/userguide/random.xml
new file mode 100644
index 0000000..585c244
--- /dev/null
+++ b/src/site/xdoc/userguide/random.xml
@@ -0,0 +1,538 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 1067592 $ $Date: 2011-02-06 06:42:35 +0100 (dim. 06 févr. 2011) $ -->
+<document url="random.html">
+
+<properties>
+ <title>The Commons Math User Guide - Data Generation</title>
+</properties>
+
+<body>
+
+<section name="2 Data Generation">
+
+<subsection name="2.1 Overview" href="overview">
+ <p>
+ The Commons Math random package includes utilities for
+ <ul>
+ <li>generating random numbers</li>
+ <li>generating random vectors</li>
+ <li>generating random strings</li>
+ <li>generating cryptographically secure sequences of random numbers or
+ strings</li>
+ <li>generating random samples and permutations</li>
+ <li>analyzing distributions of values in an input file and generating
+ values "like" the values in the file</li>
+ <li>generating data for grouped frequency distributions or
+ histograms</li>
+ </ul></p>
+ <p>
+ The source of random data used by the data generation utilities is
+ pluggable. By default, the JDK-supplied PseudoRandom Number Generator
+ (PRNG) is used, but alternative generators can be "plugged in" using an
+ adaptor framework, which provides a generic facility for replacing
+ <code>java.util.Random</code> with an alternative PRNG. Other very
+ good PRNG suitable for Monte-Carlo analysis (but <strong>not</strong>
+ for cryptography) provided by the library are the Mersenne twister from
+ Makoto Matsumoto and Takuji Nishimura and the more recent WELL generators
+ (Well Equidistributed Long-period Linear) from François Panneton, Pierre
+ L'Ecuyer and Makoto Matsumoto.
+ </p>
+ <p>
+ Sections 2.2-2.6 below show how to use the commons math API to generate
+ different kinds of random data. The examples all use the default
+ JDK-supplied PRNG. PRNG pluggability is covered in 2.7. The only
+ modification required to the examples to use alternative PRNGs is to
+ replace the argumentless constructor calls with invocations including
+ a <code>RandomGenerator</code> instance as a parameter.
+ </p>
+</subsection>
+
+<subsection name="2.2 Random numbers" href="deviates">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/random/RandomData.html">
+ RandomData</a> interface defines methods for generating random sequences
+ of numbers. The API contracts of these methods use the following concepts:
+ <dl>
+ <dt>Random sequence of numbers from a probability distribution</dt>
+ <dd>There is no such thing as a single "random number." What can be
+ generated are <i>sequences</i> of numbers that appear to be random. When
+ using the built-in JDK function <code>Math.random(),</code> sequences of
+ values generated follow the
+ <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">
+ Uniform Distribution</a>, which means that the values are evenly spread
+ over the interval between 0 and 1, with no sub-interval having a greater
+ probability of containing generated values than any other interval of the
+ same length. The mathematical concept of a
+ <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda36.htm">
+ probability distribution</a> basically amounts to asserting that different
+ ranges in the set of possible values of a random variable have
+ different probabilities of containing the value. Commons Math supports
+ generating random sequences from each of the distributions in the
+ <a href="../apidocs/org/apache/commons/math/distribution/package-summary.html">
+ distributions</a> package.
+ The javadoc for the <code>nextXxx</code> methods in
+ <a href="../apidocs/org/apache/commons/math/random/RandomDataImpl.html">
+ RandomDataImpl</a> describes the algorithms used to generate
+ random deviates.
+ </dd>
+ <dt>Cryptographically secure random sequences</dt>
+ <dd>It is possible for a sequence of numbers to appear random, but
+ nonetheless to be predictable based on the algorithm used to generate the
+ sequence. If in addition to randomness, strong unpredictability is
+ required, it is best to use a
+ <a href="http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
+ secure random number generator</a> to generate values (or strings). The
+ nextSecureXxx methods in the <code>RandomDataImpl</code> implementation of
+ the <code>RandomData</code> interface use the JDK <code>SecureRandom</code>
+ PRNG to generate cryptographically secure sequences. The
+ <code>setSecureAlgorithm</code> method allows you to change the underlying
+ PRNG. These methods are <strong>much slower</strong> than the corresponding
+ "non-secure" versions, so they should only be used when cryptographic
+ security is required.</dd>
+ <dt>Seeding pseudo-random number generators</dt>
+ <dd>By default, the implementation provided in <code>RandomDataImpl</code>
+ uses the JDK-provided PRNG. Like most other PRNGs, the JDK generator
+ generates sequences of random numbers based on an initial "seed value".
+ For the non-secure methods, starting with the same seed always produces the
+ same sequence of values. Secure sequences started with the same seeds will
+ diverge. When a new <code>RandomDataImpl</code> is created, the underlying
+ random number generators are <strong>not</strong> initialized. The first
+ call to a data generation method, or to a <code>reSeed()</code> method
+ initializes the appropriate generator. If you do not explicitly seed the
+ generator, it is by default seeded with the current time in milliseconds.
+ Therefore, to generate sequences of random data values, you should always
+ instantiate <strong>one</strong> <code> RandomDataImpl</code> and use it
+ repeatedly instead of creating new instances for subsequent values in the
+ sequence. For example, the following will generate a random sequence of 50
+ long integers between 1 and 1,000,000, using the current time in
+ milliseconds as the seed for the JDK PRNG:
+ <source>
+RandomData randomData = new RandomDataImpl();
+for (int i = 0; i < 1000; i++) {
+ value = randomData.nextLong(1, 1000000);
+}
+ </source>
+ The following will not in general produce a good random sequence, since the
+ PRNG is reseeded each time through the loop with the current time in
+ milliseconds:
+ <source>
+for (int i = 0; i < 1000; i++) {
+ RandomDataImpl randomData = new RandomDataImpl();
+ value = randomData.nextLong(1, 1000000);
+}
+ </source>
+ The following will produce the same random sequence each time it is
+ executed:
+ <source>
+RandomData randomData = new RandomDataImpl();
+randomData.reSeed(1000);
+for (int i = 0; i = 1000; i++) {
+ value = randomData.nextLong(1, 1000000);
+}
+ </source>
+ The following will produce a different random sequence each time it is
+ executed.
+ <source>
+RandomData randomData = new RandomDataImpl();
+randomData.reSeedSecure(1000);
+for (int i = 0; i < 1000; i++) {
+ value = randomData.nextSecureLong(1, 1000000);
+}
+ </source>
+ </dd></dl>
+ </p>
+</subsection>
+
+<subsection name="2.3 Random Vectors" href="vectors">
+ <p>
+ Some algorithms require random vectors instead of random scalars. When the
+ components of these vectors are uncorrelated, they may be generated simply
+ one at a time and packed together in the vector. The <a
+ href="../apidocs/org/apache/commons/math/random/UncorrelatedRandomVectorGenerator.html">
+ UncorrelatedRandomVectorGenerator</a> class simplifies this
+ process by setting the mean and deviation of each component once and
+ generating complete vectors. When the components are correlated however,
+ generating them is much more difficult. The <a href="../apidocs/org/apache/commons/math/random/CorrelatedRandomVectorGenerator.html">
+ CorrelatedRandomVectorGenerator</a> class provides this service. In this
+ case, the user must set up a complete covariance matrix instead of a simple
+ standard deviations vector. This matrix gathers both the variance and the
+ correlation information of the probability law.
+ </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
+ common 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>.
+ </p>
+ <p><dl>
+ <dt>Generating random vectors from a bivariate normal distribution</dt><dd>
+ <source>
+// Create and seed a RandomGenerator (could use any of the generators in the random package here)
+RandomGenerator rg = new JDKRandomGenerator();
+rg.setSeed(17399225432l); // Fixed seed means same results every time
+
+// Create a GassianRandomGenerator using rg as its source of randomness
+GaussianRandomGenerator rawGenerator = new GaussianRandomGenerator(rg);
+
+// Create a CorrelatedRandomVectorGenerator using rawGenerator for the components
+CorrelatedRandomVectorGenerator generator =
+ new CorrelatedRandomVectorGenerator(mean, covariance, 1.0e-12 * covariance.getNorm(), rawGenerator);
+
+// Use the generator to generate correlated vectors
+double[] randomVector = generator.nextVector();
+... </source>
+ The <code>mean</code> argument is a double[] array holding the means of the random vector
+ components. In the bivariate case, it must have length 2. The <code>covariance</code> argument
+ is a RealMatrix, which needs to be 2 x 2. The main diagonal elements are the
+ variances of the vector components and the off-diagonal elements are the covariances.
+ For example, if the means are 1 and 2 respectively, and the desired standard deviations
+ are 3 and 4, respectively, then we need to use
+ <source>
+double[] mean = {1, 2};
+double[][] cov = {{9, c}, {c, 16}};
+RealMatrix covariance = MatrixUtils.createRealMatrix(cov); </source>
+ where c is the desired covariance. If you are starting with a desired correlation,
+ you need to translate this to a covariance by multiplying it by the product of the
+ standard deviations. For example, if you want to generate data that will give Pearson's
+ R of 0.5, you would use c = 3 * 4 * .5 = 6.
+ </dd></dl></p>
+ <p>
+ In addition to multivariate normal distributions, correlated vectors from multivariate uniform
+ distributions can be generated by creating a
+ <a href="../apidocs/org/apache/commons/math/random/UniformRandomGenerator.html">UniformRandomGenerator</a>
+ in place of the
+ <code>GaussianRandomGenerator</code> above. More generally, any
+ <a href="../apidocs/org/apache/commons/math/random/NormalizedRandomGenerator.html">NormalizedRandomGenerator</a>
+ may be used.
+ </p>
+</subsection>
+
+<subsection name="2.4 Random Strings" href="strings">
+ <p>
+ The methods <code>nextHexString</code> and <code>nextSecureHexString</code>
+ can be used to generate random strings of hexadecimal characters. Both
+ of these methods produce sequences of strings with good dispersion
+ properties. The difference between the two methods is that the second is
+ cryptographically secure. Specifically, the implementation of
+ <code>nextHexString(n)</code> in <code>RandomDataImpl</code> uses the
+ following simple algorithm to generate a string of <code>n</code> hex digits:
+ <ol>
+ <li>n/2+1 binary bytes are generated using the underlying Random</li>
+ <li>Each binary byte is translated into 2 hex digits</li></ol>
+ The <code>RandomDataImpl</code> implementation of the "secure" version,
+ <code>nextSecureHexString</code> generates hex characters in 40-byte
+ "chunks" 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>
+ Similarly to the secure random number generation methods,
+ <code>nextSecureHexString</code> is <strong>much slower</strong> than
+ the non-secure version. It should be used only for applications such as
+ generating unique session or transaction ids where predictability of
+ subsequent ids based on observation of previous values is a security
+ concern. If all that is needed is an even distribution of hex characters
+ in the generated strings, the non-secure method should be used.
+ </p>
+</subsection>
+
+<subsection name="2.5 Random permutations, combinations, sampling"
+ href="combinatorics">
+ <p>
+ To select a random sample of objects in a collection, you can use the
+ <code>nextSample</code> method in the <code>RandomData</code> interface.
+ Specifically, if <code>c</code> is a collection containing at least
+ <code>k</code> objects, and <code>randomData</code> is a
+ <code>RandomData</code> instance <code>randomData.nextSample(c, k)</code>
+ will return an <code>object[]</code> array of length <code>k</code>
+ consisting of elements randomly selected from the collection. If
+ <code>c</code> contains duplicate references, there may be duplicate
+ references in the returned array; otherwise returned elements will be
+ unique -- i.e., the sampling is without replacement among the object
+ references in the collection. </p>
+ <p>
+ If <code>randomData</code> is a <code>RandomData</code> instance, and
+ <code>n</code> and <code>k</code> are integers with
+ <code> k <= n</code>, then
+ <code>randomData.nextPermutation(n, k)</code> returns an <code>int[]</code>
+ array of length <code>k</code> whose whose entries are selected randomly,
+ without repetition, from the integers <code>0</code> through
+ <code>n-1</code> (inclusive), i.e.,
+ <code>randomData.nextPermutation(n, k)</code> returns a random
+ permutation of <code>n</code> taken <code>k</code> at a time.
+ </p>
+</subsection>
+
+<subsection name="2.6 Generating data 'like' an input file" href="empirical">
+ <p>
+ Using the <code>ValueServer</code> class, you can generate data based on
+ the values in an input file in one of two ways:
+ <dl>
+ <dt>Replay Mode</dt>
+ <dd> The following code will read data from <code>url</code>
+ (a <code>java.net.URL</code> instance), cycling through the values in the
+ file in sequence, reopening and starting at the beginning again when all
+ values have been read.
+ <source>
+ ValueServer vs = new ValueServer();
+ vs.setValuesFileURL(url);
+ vs.setMode(ValueServer.REPLAY_MODE);
+ vs.resetReplayFile();
+ double value = vs.getNext();
+ // ...Generate and use more values...
+ vs.closeReplayFile();
+ </source>
+ The values in the file are not stored in memory, so it does not matter
+ how large the file is, but you do need to explicitly close the file
+ as above. The expected file format is \n -delimited (i.e. one per line)
+ strings representing valid floating point numbers.
+ </dd>
+ <dt>Digest Mode</dt>
+ <dd>When used in Digest Mode, the ValueServer reads the entire input file
+ and estimates a probability density function based on data from the file.
+ The estimation method is essentially the
+ <a href="http://nedwww.ipac.caltech.edu/level5/March02/Silverman/Silver2_6.html">
+ Variable Kernel Method</a> with Gaussian smoothing. Once the density
+ has been estimated, <code>getNext()</code> returns random values whose
+ probability distribution matches the empirical distribution -- i.e., if
+ you generate a large number of such values, their distribution should
+ "look like" the distribution of the values in the input file. The values
+ are not stored in memory in this case either, so there is no limit to the
+ size of the input file. Here is an example:
+ <source>
+ ValueServer vs = new ValueServer();
+ vs.setValuesFileURL(url);
+ vs.setMode(ValueServer.DIGEST_MODE);
+ vs.computeDistribution(500); //Read file and estimate distribution using 500 bins
+ double value = vs.getNext();
+ // ...Generate and use more values...
+ </source>
+ See the javadoc for <code>ValueServer</code> and
+ <code>EmpiricalDistribution</code> for more details. Note that
+ <code>computeDistribution()</code> opens and closes the input file
+ by itself.
+ </dd>
+ </dl>
+ </p>
+</subsection>
+
+<subsection name="2.7 PRNG Pluggability" href="pluggability">
+ <p>
+ To enable alternative PRNGs to be "plugged in" to the commons-math data
+ generation utilities and to provide a generic means to replace
+ <code>java.util.Random</code> in applications, a random generator
+ adaptor framework has been added to commons-math. The
+ <a href="../apidocs/org/apache/commons/math/random/RandomGenerator.html">
+ RandomGenerator</a> interface abstracts the public interface of
+ <code>java.util.Random</code> and any implementation of this
+ interface can be used as the source of random data for the commons-math
+ data generation classes. An abstract base class,
+ <a href="../apidocs/org/apache/commons/math/random/AbstractRandomGenerator.html">
+ AbstractRandomGenerator</a> is provided to make implementation easier.
+ This class provides default implementations of "derived" data generation
+ methods based on the primitive, <code>nextDouble()</code>.
+ To support generic replacement of <code>java.util.Random</code>, the
+ <a href="../apidocs/org/apache/commons/math/random/RandomAdaptor.html">
+ RandomAdaptor</a> class is provided, which extends
+ <code>java.util.Random</code> and wraps and delegates calls to
+ a <code>RandomGenerator</code> instance.
+ </p>
+
+ <p>Commons-math provides by itself several implementations of the <a
+ href="../apidocs/org/apache/commons/math/random/RandomGenerator.html">
+ RandomGenerator</a> interface:
+ <ul>
+ <li><a href="../apidocs/org/apache/commons/math/random/JDKRandomGenerator.html">JDKRandomGenerator</a>
+ that extends the JDK provided generator</li>
+ <li><a href="../apidocs/org/apache/commons/math/random/AbstractRandomGenerator.html">
+ AbstractRandomGenerator</a> as a helper for users generators</li>
+ <li><a href="../apidocs/org/apache/commons/math/random/BitStreamGenerator.html">
+ BitStreamGenerator</a> which is an abstract class for several generators and
+ which in turn is extended by:
+ <ul>
+ <li><a href="../apidocs/org/apache/commons/math/random/MersenneTwister.html">MersenneTwister</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well512a.html">Well512a</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well1024a.html">Well1024a</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well19937a.html">Well19937a</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well19937c.html">Well19937c</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well44497a.html">Well44497a</a></li>
+ <li><a href="../apidocs/org/apache/commons/math/random/Well44497b.html">Well44497b</a></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ç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 between 25 and 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><a href="../apidocs/org/apache/commons/math/random/MersenneTwister.html">MersenneTwister</a></td><td>1</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/JDKRandomGenerator.html">JDKRandomGenerator</a></td><td>between 0.96 and 1.16</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well512a.html">Well512a</a></td><td>between 0.85 and 0.88</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well1024a.html">Well1024a</a></td><td>between 0.63 and 0.73</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well19937a.html">Well19937a</a></td><td>between 0.70 and 0.71</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well19937c.html">Well19937c</a></td><td>between 0.57 and 0.71</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well44497a.html">Well44497a</a></td><td>between 0.69 and 0.71</td></tr>
+ <tr><td><a href="../apidocs/org/apache/commons/math/random/Well44497b.html">Well44497b</a></td><td>between 0.65 and 0.71</td></tr>
+ </table>
+ </p>
+
+ <p>
+ So for most simulation problems, the better generators like <a
+ href="../apidocs/org/apache/commons/math/random/Well19937c.html">Well19937c</a> and <a
+ href="../apidocs/org/apache/commons/math/random/Well44497b.html">Well44497b</a> 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>
+
+ <p>
+ Examples:
+ <dl>
+ <dt>Create a RandomGenerator based on RngPack's Mersenne Twister</dt>
+ <dd>To create a RandomGenerator using the RngPack Mersenne Twister PRNG
+ as the source of randomness, extend <code>AbstractRandomGenerator</code>
+ overriding the derived methods that the RngPack implementation provides:
+ <source>
+import edu.cornell.lassp.houle.RngPack.RanMT;
+/**
+ * AbstractRandomGenerator based on RngPack RanMT generator.
+ */
+public class RngPackGenerator extends AbstractRandomGenerator {
+
+ private RanMT random = new RanMT();
+
+ public void setSeed(long seed) {
+ random = new RanMT(seed);
+ }
+
+ public double nextDouble() {
+ return random.raw();
+ }
+
+ public double nextGaussian() {
+ return random.gaussian();
+ }
+
+ public int nextInt(int n) {
+ return random.choose(n);
+ }
+
+ public boolean nextBoolean() {
+ return random.coin();
+ }
+}
+ </source>
+ </dd>
+ <dt>Use the Mersenne Twister RandomGenerator in place of
+ <code>java.util.Random</code> in <code>RandomData</code></dt>
+ <dd>
+ <source>
+RandomData randomData = new RandomDataImpl(new RngPackGenerator());
+ </source>
+ </dd>
+ <dt>Create an adaptor instance based on the Mersenne Twister generator
+ that can be used in place of a <code>Random</code></dt>
+ <dd>
+ <source>
+ RandomGenerator generator = new RngPackGenerator();
+ Random random = RandomAdaptor.createAdaptor(generator);
+ // random can now be used in place of a Random instance, data generation
+ // calls will be delegated to the wrapped Mersenne Twister
+ </source>
+ </dd>
+ </dl>
+ </p>
+</subsection>
+
+</section>
+
+</body>
+</document>
diff --git a/src/site/xdoc/userguide/special.xml b/src/site/xdoc/userguide/special.xml
new file mode 100644
index 0000000..885d273
--- /dev/null
+++ b/src/site/xdoc/userguide/special.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="special.html">
+ <properties>
+ <title>The Commons Math User Guide - Special Functions</title>
+ </properties>
+ <body>
+ <section name="5 Special Functions">
+ <subsection name="5.1 Overview" href="overview">
+ <p>
+ The special functions portion of Commons-Math contains several useful functions not
+ provided by <code>java.lang.Math</code>. These functions mostly serve as building blocks
+ for other portions of Commons-Math but, as others may find them useful as stand-alone
+ methods, these special functions were included as part of the Commons-Math public API.
+ </p>
+ </subsection>
+ <subsection name="5.2 Erf functions" href="erf">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/special/Erf.html">Erf</a> contains
+ several useful functions involving the Error Function, Erf.
+ <table>
+ <tr><th>Function</th><th>Method</th><th>Reference</th></tr>
+ <tr><td>Error Function</td><td>erf</td><td>See <a href="http://mathworld.wolfram.com/Erf.html">Erf</a> from MathWorld</td></tr>
+ </table>
+ </p>
+ </subsection>
+ <subsection name="5.3 Gamma functions" href="gamma">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/special/Gamma.html">Gamma</a>
+ contains several useful functions involving the Gamma Function.
+ <table>
+ <tr><th>Function</th><th>Method</th><th>Reference</th></tr>
+ <tr><td>Log Gamma</td><td>logGamma</td><td>See <a href="http://mathworld.wolfram.com/GammaFunction.html">Gamma Function</a> from MathWorld</td></tr>
+ <tr><td>Regularized Gamma</td><td>regularizedGammaP</td><td>See <a href="http://mathworld.wolfram.com/RegularizedGammaFunction.html">Regularized Gamma Function</a> from MathWorld</td></tr>
+ </table>
+ </p>
+ </subsection>
+ <subsection name="5.4 Beta funtions" href="beta">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/special/Beta.html">Beta</a>
+ contains several useful functions involving the Beta Function.
+ <table>
+ <tr><th>Function</th><th>Method</th><th>Reference</th></tr>
+ <tr><td>Log Beta</td><td>logBeta</td><td>See <a href="http://mathworld.wolfram.com/BetaFunction.html">Beta Function</a> from MathWorld</td></tr>
+ <tr><td>Regularized Beta</td><td>regularizedBeta</td><td>See <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">Regularized Beta Function</a> from MathWorld</td></tr>
+ </table>
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/stat.xml b/src/site/xdoc/userguide/stat.xml
new file mode 100644
index 0000000..5a10868
--- /dev/null
+++ b/src/site/xdoc/userguide/stat.xml
@@ -0,0 +1,1061 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 998761 $ $Date: 2010-09-20 03:57:03 +0200 (lun. 20 sept. 2010) $ -->
+<document url="stat.html">
+ <properties>
+ <title>The Commons Math User Guide - Statistics</title>
+ </properties>
+ <body>
+ <section name="1 Statistics">
+ <subsection name="1.1 Overview">
+ <p>
+ The statistics package provides frameworks and implementations for
+ basic Descriptive statistics, frequency distributions, bivariate regression,
+ and t-, chi-square and ANOVA test statistics.
+ </p>
+ <p>
+ <a href="#a1.2_Descriptive_statistics">Descriptive statistics</a><br></br>
+ <a href="#a1.3_Frequency_distributions">Frequency distributions</a><br></br>
+ <a href="#a1.4_Simple_regression">Simple Regression</a><br></br>
+ <a href="#a1.5_Multiple_linear_regression">Multiple Regression</a><br></br>
+ <a href="#a1.6_Rank_transformations">Rank transformations</a><br></br>
+ <a href="#a1.7_Covariance_and_correlation">Covariance and correlation</a><br></br>
+ <a href="#a1.8_Statistical_tests">Statistical Tests</a><br></br>
+ </p>
+ </subsection>
+ <subsection name="1.2 Descriptive statistics">
+ <p>
+ The stat package includes a framework and default implementations for
+ the following Descriptive statistics:
+ <ul>
+ <li>arithmetic and geometric means</li>
+ <li>variance and standard deviation</li>
+ <li>sum, product, log sum, sum of squared values</li>
+ <li>minimum, maximum, median, and percentiles</li>
+ <li>skewness and kurtosis</li>
+ <li>first, second, third and fourth moments</li>
+ </ul>
+ </p>
+ <p>
+ With the exception of percentiles and the median, all of these
+ statistics can be computed without maintaining the full list of input
+ data values in memory. The stat package provides interfaces and
+ implementations that do not require value storage as well as
+ implementations that operate on arrays of stored values.
+ </p>
+ <p>
+ The top level interface is
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/UnivariateStatistic.html">
+ UnivariateStatistic</a>.
+ This interface, implemented by all statistics, consists of
+ <code>evaluate()</code> methods that take double[] arrays as arguments
+ and return the value of the statistic. This interface is extended by
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatistic.html">
+ StorelessUnivariateStatistic</a>, which adds <code>increment(),</code>
+ <code>getResult()</code> and associated methods to support
+ "storageless" implementations that maintain counters, sums or other
+ state information as values are added using the <code>increment()</code>
+ method.
+ </p>
+ <p>
+ Abstract implementations of the top level interfaces are provided in
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatistic.html">
+ AbstractUnivariateStatistic</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/AbstractStorelessUnivariateStatistic.html">
+ AbstractStorelessUnivariateStatistic</a> respectively.
+ </p>
+ <p>
+ Each statistic is implemented as a separate class, in one of the
+ subpackages (moment, rank, summary) and each extends one of the abstract
+ classes above (depending on whether or not value storage is required to
+ compute the statistic). There are several ways to instantiate and use statistics.
+ Statistics can be instantiated and used directly, but it is generally more convenient
+ (and efficient) to access them using the provided aggregates,
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.html">
+ DescriptiveStatistics</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/SummaryStatistics.html">
+ SummaryStatistics.</a>
+ </p>
+ <p>
+ <code>DescriptiveStatistics</code> maintains the input data in memory
+ and has the capability of producing "rolling" statistics computed from a
+ "window" consisting of the most recently added values.
+ </p>
+ <p>
+ <code>SummaryStatistics</code> does not store the input data values
+ in memory, so the statistics included in this aggregate are limited to those
+ that can be computed in one pass through the data without access to
+ the full array of values.
+ </p>
+ <p>
+ <table>
+ <tr><th>Aggregate</th><th>Statistics Included</th><th>Values stored?</th>
+ <th>"Rolling" capability?</th></tr><tr><td>
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/DescriptiveStatistics.html">
+ DescriptiveStatistics</a></td><td>min, max, mean, geometric mean, n,
+ sum, sum of squares, standard deviation, variance, percentiles, skewness,
+ kurtosis, median</td><td>Yes</td><td>Yes</td></tr><tr><td>
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/SummaryStatistics.html">
+ SummaryStatistics</a></td><td>min, max, mean, geometric mean, n,
+ sum, sum of squares, standard deviation, variance</td><td>No</td><td>No</td></tr>
+ </table>
+ </p>
+ <p>
+ <code>SummaryStatistics</code> can be aggregated using
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.html">
+ AggregateSummaryStatistics.</a> This class can be used to concurrently
+ gather statistics for multiple datasets as well as for a combined sample
+ including all of the data.
+ </p>
+ <p>
+ <code>MultivariateSummaryStatistics</code> is similar to
+ <code>SummaryStatistics</code> but handles n-tuple values instead of
+ scalar values. It can also compute the full covariance matrix for the
+ input data.
+ </p>
+ <p>
+ Neither <code>DescriptiveStatistics</code> nor <code>SummaryStatistics</code>
+ is thread-safe.
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatistics.html">
+ SynchronizedDescriptiveStatistics</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatistics.html">
+ SynchronizedSummaryStatistics</a>, respectively, provide thread-safe
+ versions for applications that require concurrent access to statistical
+ aggregates by multiple threads.
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/SynchronizedMultiVariateSummaryStatistics.html">
+ SynchronizedMultivariateSummaryStatistics</a> provides thread-safe
+ <code>MultivariateSummaryStatistics.</code>
+ </p>
+ <p>
+ There is also a utility class,
+ <a href="../apidocs/org/apache/commons/math/stat/StatUtils.html">
+ StatUtils</a>, that provides static methods for computing statistics
+ directly from double[] arrays.
+ </p>
+ <p>
+ Here are some examples showing how to compute Descriptive statistics.
+ <dl>
+ <dt>Compute summary statistics for a list of double values</dt>
+ <br></br>
+ <dd>Using the <code>DescriptiveStatistics</code> aggregate
+ (values are stored in memory):
+ <source>
+// Get a DescriptiveStatistics instance
+DescriptiveStatistics stats = new DescriptiveStatistics();
+
+// Add the data from the array
+for( int i = 0; i < inputArray.length; i++) {
+ stats.addValue(inputArray[i]);
+}
+
+// Compute some statistics
+double mean = stats.getMean();
+double std = stats.getStandardDeviation();
+double median = stats.getMedian();
+ </source>
+ </dd>
+ <dd>Using the <code>SummaryStatistics</code> aggregate (values are
+ <strong>not</strong> stored in memory):
+ <source>
+// Get a SummaryStatistics instance
+SummaryStatistics stats = new SummaryStatistics();
+
+// Read data from an input stream,
+// adding values and updating sums, counters, etc.
+while (line != null) {
+ line = in.readLine();
+ stats.addValue(Double.parseDouble(line.trim()));
+}
+in.close();
+
+// Compute the statistics
+double mean = stats.getMean();
+double std = stats.getStandardDeviation();
+//double median = stats.getMedian(); <-- NOT AVAILABLE
+ </source>
+ </dd>
+ <dd>Using the <code>StatUtils</code> utility class:
+ <source>
+// Compute statistics directly from the array
+// assume values is a double[] array
+double mean = StatUtils.mean(values);
+double std = StatUtils.variance(values);
+double median = StatUtils.percentile(50);
+
+// Compute the mean of the first three values in the array
+mean = StatUtils.mean(values, 0, 3);
+ </source>
+ </dd>
+ <dt>Maintain a "rolling mean" of the most recent 100 values from
+ an input stream</dt>
+ <br></br>
+ <dd>Use a <code>DescriptiveStatistics</code> instance with
+ window size set to 100
+ <source>
+// Create a DescriptiveStats instance and set the window size to 100
+DescriptiveStatistics stats = new DescriptiveStatistics();
+stats.setWindowSize(100);
+
+// Read data from an input stream,
+// displaying the mean of the most recent 100 observations
+// after every 100 observations
+long nLines = 0;
+while (line != null) {
+ line = in.readLine();
+ stats.addValue(Double.parseDouble(line.trim()));
+ if (nLines == 100) {
+ nLines = 0;
+ System.out.println(stats.getMean());
+ }
+}
+in.close();
+ </source>
+ </dd>
+ <dt>Compute statistics in a thread-safe manner</dt>
+ <br/>
+ <dd>Use a <code>SynchronizedDescriptiveStatistics</code> instance
+ <source>
+// Create a SynchronizedDescriptiveStatistics instance and
+// use as any other DescriptiveStatistics instance
+DescriptiveStatistics stats = new SynchronizedDescriptiveStatistics();
+ </source>
+ </dd>
+ <dt>Compute statistics for multiple samples and overall statistics concurrently</dt>
+ <br/>
+ <dd>There are two ways to do this using <code>AggregateSummaryStatistics.</code>
+ The first is to use an <code>AggregateSummaryStatistics</code> instance
+ to accumulate overall statistics contributed by <code>SummaryStatistics</code>
+ instances created using
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.html#createContributingStatistics()">
+ AggregateSummaryStatistics.createContributingStatistics()</a>:
+ <source>
+// Create a AggregateSummaryStatistics instance to accumulate the overall statistics
+// and AggregatingSummaryStatistics for the subsamples
+AggregateSummaryStatistics aggregate = new AggregateSummaryStatistics();
+SummaryStatistics setOneStats = aggregate.createContributingStatistics();
+SummaryStatistics setTwoStats = aggregate.createContributingStatistics();
+// Add values to the subsample aggregates
+setOneStats.addValue(2);
+setOneStats.addValue(3);
+setTwoStats.addValue(2);
+setTwoStats.addValue(4);
+...
+// Full sample data is reported by the aggregate
+double totalSampleSum = aggregate.getSum();
+ </source>
+ The above approach has the disadvantages that the <code>addValue</code> calls must be synchronized on the
+ <code>SummaryStatistics</code> instance maintained by the aggregate and each value addition updates the
+ aggregate as well as the subsample. For applications that can wait to do the aggregation until all values
+ have been added, a static
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/AggregateSummaryStatistics.html#aggregate(java.util.Collection)">
+ aggregate</a> method is available, as shown in the following example.
+ This method should be used when aggregation needs to be done across threads.
+ <source>
+// Create SummaryStatistics instances for the subsample data
+SummaryStatistics setOneStats = new SummaryStatistics();
+SummaryStatistics setTwoStats = new SummaryStatistics();
+// Add values to the subsample SummaryStatistics instances
+setOneStats.addValue(2);
+setOneStats.addValue(3);
+setTwoStats.addValue(2);
+setTwoStats.addValue(4);
+...
+// Aggregate the subsample statistics
+Collection<SummaryStatistics> aggregate = new ArrayList<SummaryStatistics>();
+aggregate.add(setOneStats);
+aggregate.add(setTwoStats);
+StatisticalSummary aggregatedStats = AggregateSummaryStatistics.aggregate(aggregate);
+
+// Full sample data is reported by aggregatedStats
+double totalSampleSum = aggregatedStats.getSum();
+ </source>
+ </dd>
+ </dl>
+ </p>
+ </subsection>
+ <subsection name="1.3 Frequency distributions">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/stat/Frequency.html">
+ Frequency</a>
+ provides a simple interface for maintaining counts and percentages of discrete
+ values.
+ </p>
+ <p>
+ Strings, integers, longs and chars are all supported as value types,
+ as well as instances of any class that implements <code>Comparable.</code>
+ The ordering of values used in computing cumulative frequencies is by
+ default the <i>natural ordering,</i> but this can be overriden by supplying a
+ <code>Comparator</code> to the constructor. Adding values that are not
+ comparable to those that have already been added results in an
+ <code>IllegalArgumentException.</code>
+ </p>
+ <p>
+ Here are some examples.
+ <dl>
+ <dt>Compute a frequency distribution based on integer values</dt>
+ <br></br>
+ <dd>Mixing integers, longs, Integers and Longs:
+ <source>
+ Frequency f = new Frequency();
+ f.addValue(1);
+ f.addValue(new Integer(1));
+ f.addValue(new Long(1));
+ f.addValue(2);
+ f.addValue(new Integer(-1));
+ System.out.prinltn(f.getCount(1)); // displays 3
+ System.out.println(f.getCumPct(0)); // displays 0.2
+ System.out.println(f.getPct(new Integer(1))); // displays 0.6
+ System.out.println(f.getCumPct(-2)); // displays 0
+ System.out.println(f.getCumPct(10)); // displays 1
+ </source>
+ </dd>
+ <dt>Count string frequencies</dt>
+ <br></br>
+ <dd>Using case-sensitive comparison, alpha sort order (natural comparator):
+ <source>
+Frequency f = new Frequency();
+f.addValue("one");
+f.addValue("One");
+f.addValue("oNe");
+f.addValue("Z");
+System.out.println(f.getCount("one")); // displays 1
+System.out.println(f.getCumPct("Z")); // displays 0.5
+System.out.println(f.getCumPct("Ot")); // displays 0.25
+ </source>
+ </dd>
+ <dd>Using case-insensitive comparator:
+ <source>
+Frequency f = new Frequency(String.CASE_INSENSITIVE_ORDER);
+f.addValue("one");
+f.addValue("One");
+f.addValue("oNe");
+f.addValue("Z");
+System.out.println(f.getCount("one")); // displays 3
+System.out.println(f.getCumPct("z")); // displays 1
+ </source>
+ </dd>
+ </dl>
+ </p>
+ </subsection>
+ <subsection name="1.4 Simple regression">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/stat/regression/SimpleRegression.html">
+ SimpleRegression</a> provides ordinary least squares regression with
+ one independent variable estimating the linear model:
+ </p>
+ <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>
+ <p>
+ <strong>Implementation Notes</strong>: <ul>
+ <li> As observations are added to the model, the sum of x values, y values,
+ cross products (x times y), and squared deviations of x and y from their
+ respective means are updated using updating formulas 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. All regression
+ statistics are computed from these sums.</li>
+ <li> Inference statistics (confidence intervals, parameter significance levels)
+ are based on 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></li>
+ </ul>
+ </p>
+ <p>
+ Here are some examples.
+ <dl>
+ <dt>Estimate a model based on observations added one at a time</dt>
+ <br></br>
+ <dd>Instantiate a regression instance and add data points
+ <source>
+regression = new SimpleRegression();
+regression.addData(1d, 2d);
+// At this point, with only one observation,
+// all regression statistics will return NaN
+
+regression.addData(3d, 3d);
+// With only two observations,
+// slope and intercept can be computed
+// but inference statistics will return NaN
+
+regression.addData(3d, 3d);
+// Now all statistics are defined.
+ </source>
+ </dd>
+ <dd>Compute some statistics based on observations added so far
+ <source>
+System.out.println(regression.getIntercept());
+// displays intercept of regression line
+
+System.out.println(regression.getSlope());
+// displays slope of regression line
+
+System.out.println(regression.getSlopeStdErr());
+// displays slope standard error
+ </source>
+ </dd>
+ <dd>Use the regression model to predict the y value for a new x value
+ <source>
+System.out.println(regression.predict(1.5d)
+// displays predicted y value for x = 1.5
+ </source>
+ More data points can be added and subsequent getXxx calls will incorporate
+ additional data in statistics.
+ </dd>
+ <dt>Estimate a model from a double[][] array of data points</dt>
+ <br></br>
+ <dd>Instantiate a regression object and load dataset
+ <source>
+double[][] data = { { 1, 3 }, {2, 5 }, {3, 7 }, {4, 14 }, {5, 11 }};
+SimpleRegression regression = new SimpleRegression();
+regression.addData(data);
+ </source>
+ </dd>
+ <dd>Estimate regression model based on data
+ <source>
+System.out.println(regression.getIntercept());
+// displays intercept of regression line
+
+System.out.println(regression.getSlope());
+// displays slope of regression line
+
+System.out.println(regression.getSlopeStdErr());
+// displays slope standard error
+ </source>
+ More data points -- even another double[][] array -- can be added and subsequent
+ getXxx calls will incorporate additional data in statistics.
+ </dd>
+ </dl>
+ </p>
+ </subsection>
+ <subsection name="1.5 Multiple linear regression">
+ <p>
+ <a href="../apidocs/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.html">
+ OLSMultipleLinearRegression</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.html">
+ GLSMultipleLinearRegression</a> provide least squares regression to fit the linear model:
+ </p>
+ <p>
+ <code> Y=X*b+u </code>
+ </p>
+ <p>
+ where Y is an n-vector <b>regressand</b>, X is a [n,k] matrix whose k columns are called
+ <b>regressors</b>, b is k-vector of <b>regression parameters</b> and u is an n-vector
+ of <b>error terms</b> or <b>residuals</b>.
+ </p>
+ <p>
+ <a href="../apidocs/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.html">
+ OLSMultipleLinearRegression</a> provides Ordinary Least Squares Regression, and
+ <a href="../apidocs/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.html">
+ GLSMultipleLinearRegression</a> implements Generalized Least Squares. See the javadoc for these
+ classes for details on the algorithms and forumlas used.
+ </p>
+ <p>
+ Data for OLS models can be loaded in a single double[] array, consisting of concatenated rows of data, each containing
+ the regressand (Y) value, followed by regressor values; or using a double[][] array with rows corresponding to
+ observations. GLS models also require a double[][] array representing the covariance matrix of the error terms. See
+ <a href="../apidocs/org/apache/commons/math/stat/regression/AbstractMultipleLinearRegression.html#newSampleData(double[], int, int)">
+ AbstractMultipleLinearRegression#newSampleData(double[],int,int)</a>,
+ <a href="../apidocs/org/apache/commons/math/stat/regression/OLSMultipleLinearRegression.html#newSampleData(double[], double[][])">
+ OLSMultipleLinearRegression#newSampleData(double[], double[][])</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/regression/GLSMultipleLinearRegression.html#newSampleData(double[], double[][], double[][])">
+ GLSMultipleLinearRegression#newSampleData(double[],double[][],double[][])</a> for details.
+ </p>
+ <p>
+ <strong>Usage Notes</strong>: <ul>
+ <li> Data are validated when invoking any of the newSample, newX, newY or newCovariance methods and
+ <code>IllegalArgumentException</code> is thrown when input data arrays do not have matching dimensions
+ or do not contain sufficient data to estimate the model.
+ </li>
+ <li> By default, regression models are estimated with intercept terms. In the notation above, this implies that the
+ X matrix contains an initial row identically equal to 1. X data supplied to the newX or newSample methods should not
+ include this column - the data loading methods will create it automatically. To estimate a model without an intercept
+ term, set the <code>noIntercept</code> property to <code>true.</code></li>
+ </ul>
+ </p>
+ <p>
+ Here are some examples.
+ <dl>
+ <dt>OLS regression</dt>
+ <br></br>
+ <dd>Instantiate an OLS regression object and load a dataset:
+ <source>
+OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
+double[] y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+double[] x = new double[6][];
+x[0] = new double[]{0, 0, 0, 0, 0};
+x[1] = new double[]{2.0, 0, 0, 0, 0};
+x[2] = new double[]{0, 3.0, 0, 0, 0};
+x[3] = new double[]{0, 0, 4.0, 0, 0};
+x[4] = new double[]{0, 0, 0, 5.0, 0};
+x[5] = new double[]{0, 0, 0, 0, 6.0};
+regression.newSample(y, x);
+ </source>
+ </dd>
+ <dd>Get regression parameters and diagnostics:
+ <source>
+double[] beta = regression.estimateRegressionParameters();
+
+double[] residuals = regression.estimateResiduals();
+
+double[][] parametersVariance = regression.estimateRegressionParametersVariance();
+
+double regressandVariance = regression.estimateRegressandVariance();
+
+double rSquared = regression.caclulateRSquared();
+
+double sigma = regression.estimateRegressionStandardError();
+ </source>
+ </dd>
+ <dt>GLS regression</dt>
+ <br></br>
+ <dd>Instantiate a GLS regression object and load a dataset:
+ <source>
+GLSMultipleLinearRegression regression = new GLSMultipleLinearRegression();
+double[] y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+double[] x = new double[6][];
+x[0] = new double[]{0, 0, 0, 0, 0};
+x[1] = new double[]{2.0, 0, 0, 0, 0};
+x[2] = new double[]{0, 3.0, 0, 0, 0};
+x[3] = new double[]{0, 0, 4.0, 0, 0};
+x[4] = new double[]{0, 0, 0, 5.0, 0};
+x[5] = new double[]{0, 0, 0, 0, 6.0};
+double[][] omega = new double[6][];
+omega[0] = new double[]{1.1, 0, 0, 0, 0, 0};
+omega[1] = new double[]{0, 2.2, 0, 0, 0, 0};
+omega[2] = new double[]{0, 0, 3.3, 0, 0, 0};
+omega[3] = new double[]{0, 0, 0, 4.4, 0, 0};
+omega[4] = new double[]{0, 0, 0, 0, 5.5, 0};
+omega[5] = new double[]{0, 0, 0, 0, 0, 6.6};
+regression.newSampleData(y, x, omega);
+ </source>
+ </dd>
+ </dl>
+ </p>
+ </subsection>
+ <subsection name="1.6 Rank transformations">
+ <p>
+ Some statistical algorithms require that input data be replaced by ranks.
+ The <a href="../apidocs/org/apache/commons/math/stat/ranking/package-summary.html">
+ org.apache.commons.math.stat.ranking</a> package provides rank transformation.
+ <a href="../apidocs/org/apache/commons/math/stat/ranking/RankingAlgorithm.html">
+ RankingAlgorithm</a> defines the interface for ranking.
+ <a href="../apidocs/org/apache/commons/math/stat/ranking/NaturalRanking.html">
+ NaturalRanking</a> provides an implementation that has two configuration options.
+ <ul>
+ <li><a href="../apidocs/org/apache/commons/math/stat/ranking/TiesStrategy.html">
+ Ties strategy</a> deterimines how ties in the source data are handled by the ranking</li>
+ <li><a href="../apidocs/org/apache/commons/math/stat/ranking/NaNStrategy.html">
+ NaN strategy</a> determines how NaN values in the source data are handled.</li>
+ </ul>
+ </p>
+ <p>
+ Examples:
+ <source>
+NaturalRanking ranking = new NaturalRanking(NaNStrategy.MINIMAL,
+TiesStrategy.MAXIMUM);
+double[] data = { 20, 17, 30, 42.3, 17, 50,
+ Double.NaN, Double.NEGATIVE_INFINITY, 17 };
+double[] ranks = ranking.rank(exampleData);
+ </source>
+ results in <code>ranks</code> containing <code>{6, 5, 7, 8, 5, 9, 2, 2, 5}.</code>
+ <source>
+new NaturalRanking(NaNStrategy.REMOVED,TiesStrategy.SEQUENTIAL).rank(exampleData);
+ </source>
+ returns <code>{5, 2, 6, 7, 3, 8, 1, 4}.</code>
+ </p>
+ <p>
+ The default <code>NaNStrategy</code> is NaNStrategy.MAXIMAL. This makes <code>NaN</code>
+ values larger than any other value (including <code>Double.POSITIVE_INFINITY</code>). The
+ default <code>TiesStrategy</code> is <code>TiesStrategy.AVERAGE,</code> which assigns tied
+ values the average of the ranks applicable to the sequence of ties. See the
+ <a href="../apidocs/org/apache/commons/math/stat/ranking/NaturalRanking.html">
+ NaturalRanking</a> for more examples and <a href="../apidocs/org/apache/commons/math/stat/ranking/TiesStrategy.html">
+ TiesStrategy</a> and <a href="../apidocs/org/apache/commons/math/stat/ranking/NaNStrategy.html">NaNStrategy</a>
+ for details on these configuration options.
+ </p>
+ </subsection>
+ <subsection name="1.7 Covariance and correlation">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/stat/correlation/package-summary.html">
+ org.apache.commons.math.stat.correlation</a> package computes covariances
+ and correlations for pairs of arrays or columns of a matrix.
+ <a href="../apidocs/org/apache/commons/math/stat/correlation/Covariance.html">
+ Covariance</a> computes covariances,
+ <a href="../apidocs/org/apache/commons/math/stat/correlation/PearsonsCorrelation.html">
+ PearsonsCorrelation</a> provides Pearson's Product-Moment correlation coefficients and
+ <a href="../apidocs/org/apache/commons/math/stat/correlation/SpearmansCorrelation.html">
+ SpearmansCorrelation</a> computes Spearman's rank correlation.
+ </p>
+ <p>
+ <strong>Implementation Notes</strong>
+ <ul>
+ <li>
+ Unbiased covariances are given by the formula <br></br>
+ <code>cov(X, Y) = sum [(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. Non-bias-corrected estimates use
+ <code>n</code> in place of <code>n - 1.</code> Whether or not covariances are
+ bias-corrected is determined by the optional parameter, "biasCorrected," which
+ defaults to <code>true.</code>
+ </li>
+ <li>
+ <a href="../apidocs/org/apache/commons/math/stat/correlation/PearsonsCorrelation.html">
+ PearsonsCorrelation</a> computes correlations defined by the formula <br></br>
+ <code>cor(X, Y) = sum[(x<sub>i</sub> - E(X))(y<sub>i</sub> - E(Y))] / [(n - 1)s(X)s(Y)]</code><br/>
+ where <code>E(X)</code> and <code>E(Y)</code> are means of <code>X</code> and <code>Y</code>
+ and <code>s(X)</code>, <code>s(Y)</code> are standard deviations.
+ </li>
+ <li>
+ <a href="../apidocs/org/apache/commons/math/stat/correlation/SpearmansCorrelation.html">
+ SpearmansCorrelation</a> applies a rank transformation to the input data and computes Pearson's
+ correlation on the ranked data. The ranking algorithm is configurable. By default,
+ <a href="../apidocs/org/apache/commons/math/stat/ranking/NaturalRanking.html">
+ NaturalRanking</a> with default strategies for handling ties and NaN values is used.
+ </li>
+ </ul>
+ </p>
+ <p>
+ <strong>Examples:</strong>
+ <dl>
+ <dt><strong>Covariance of 2 arrays</strong></dt>
+ <br></br>
+ <dd>To compute the unbiased covariance between 2 double arrays,
+ <code>x</code> and <code>y</code>, use:
+ <source>
+new Covariance().covariance(x, y)
+ </source>
+ For non-bias-corrected covariances, use
+ <source>
+covariance(x, y, false)
+ </source>
+ </dd>
+ <br></br>
+ <dt><strong>Covariance matrix</strong></dt>
+ <br></br>
+ <dd> A covariance matrix over the columns of a source matrix <code>data</code>
+ can be computed using
+ <source>
+new Covariance().computeCovarianceMatrix(data)
+ </source>
+ The i-jth entry of the returned matrix is the unbiased covariance of the ith and jth
+ columns of <code>data.</code> As above, to get non-bias-corrected covariances,
+ use
+ <source>
+computeCovarianceMatrix(data, false)
+ </source>
+ </dd>
+ <br></br>
+ <dt><strong>Pearson's correlation of 2 arrays</strong></dt>
+ <br></br>
+ <dd>To compute the Pearson's product-moment correlation between two double arrays
+ <code>x</code> and <code>y</code>, use:
+ <source>
+new PearsonsCorrelation().correlation(x, y)
+ </source>
+ </dd>
+ <br></br>
+ <dt><strong>Pearson's correlation matrix</strong></dt>
+ <br></br>
+ <dd> A (Pearson's) correlation matrix over the columns of a source matrix <code>data</code>
+ can be computed using
+ <source>
+new PearsonsCorrelation().computeCorrelationMatrix(data)
+ </source>
+ The i-jth entry of the returned matrix is the Pearson's product-moment correlation between the
+ ith and jth columns of <code>data.</code>
+ </dd>
+ <br></br>
+ <dt><strong>Pearson's correlation significance and standard errors</strong></dt>
+ <br></br>
+ <dd> To compute standard errors and/or significances of correlation coefficients
+ associated with Pearson's correlation coefficients, start by creating a
+ <code>PearsonsCorrelation</code> instance
+ <source>
+PearsonsCorrelation correlation = new PearsonsCorrelation(data);
+ </source>
+ where <code>data</code> is either a rectangular array or a <code>RealMatrix.</code>
+ Then the matrix of standard errors is
+ <source>
+correlation.getCorrelationStandardErrors();
+ </source>
+ 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><br/>
+ where <code>r</code> is the estimated correlation coefficient and
+ <code>n</code> is the number of observations in the source dataset.<br/><br/>
+ <strong>p-values</strong> for the (2-sided) null hypotheses that elements of
+ a correlation matrix are zero populate the RealMatrix returned by
+ <source>
+correlation.getCorrelationPValues()
+ </source>
+ <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></br>
+ <code>|r<sub>ij</sub>|((n - 2) / (1 - r<sub>ij</sub><sup>2</sup>))<sup>1/2</sup></code>,
+ where <code>r<sub>ij</sub></code> is the estimated correlation between the ith and jth
+ columns of the source array or RealMatrix. This is sometimes referred to as the
+ <i>significance</i> of the coefficient.<br/><br/>
+ For example, if <code>data</code> is a RealMatrix with 2 columns and 10 rows, then
+ <source>
+new PearsonsCorrelation(data).getCorrelationPValues().getEntry(0,1)
+ </source>
+ is the significance of the Pearson's correlation coefficient between the two columns
+ of <code>data</code>. If this value is less than .01, we can say that the correlation
+ between the two columns of data is significant at the 99% level.
+ </dd>
+ <br></br>
+ <dt><strong>Spearman's rank correlation coefficient</strong></dt>
+ <br></br>
+ <dd>To compute the Spearman's rank-moment correlation between two double arrays
+ <code>x</code> and <code>y</code>:
+ <source>
+new SpearmansCorrelation().correlation(x, y)
+ </source>
+ This is equivalent to
+ <source>
+RankingAlgorithm ranking = new NaturalRanking();
+new PearsonsCorrelation().correlation(ranking.rank(x), ranking.rank(y))
+ </source>
+ </dd>
+ <br></br>
+ </dl>
+ </p>
+ </subsection>
+ <subsection name="1.8 Statistical tests">
+ <p>
+ The interfaces and implementations in the
+ <a href="../apidocs/org/apache/commons/math/stat/inference/">
+ org.apache.commons.math.stat.inference</a> package provide
+ <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc22.htm">
+ Student's t</a>,
+ <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
+ Chi-Square</a> and
+ <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc43.htm">
+ One-Way ANOVA</a> test statistics as well as
+ <a href="http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ p-values</a> associated with <code>t-</code>,
+ <code>Chi-Square</code> and <code>One-Way ANOVA</code> tests. The
+ interfaces are
+ <a href="../apidocs/org/apache/commons/math/stat/inference/TTest.html">
+ TTest</a>,
+ <a href="../apidocs/org/apache/commons/math/stat/inference/ChiSquareTest.html">
+ ChiSquareTest</a>, and
+ <a href="../apidocs/org/apache/commons/math/stat/inference/OneWayAnova.html">
+ OneWayAnova</a> with provided implementations
+ <a href="../apidocs/org/apache/commons/math/stat/inference/TTestImpl.html">
+ TTestImpl</a>,
+ <a href="../apidocs/org/apache/commons/math/stat/inference/ChiSquareTestImpl.html">
+ ChiSquareTestImpl</a> and
+ <a href="../apidocs/org/apache/commons/math/stat/inference/OneWayAnovaImpl.html">
+ OneWayAnovaImpl</a>, respectively.
+ The <a href="../apidocs/org/apache/commons/math/stat/inference/TestUtils.html">
+ TestUtils</a> class provides static methods to get test instances or
+ to compute test statistics directly. The examples below all use the
+ static methods in <code>TestUtils</code> to execute tests. To get
+ test object instances, either use e.g., <code>TestUtils.getTTest()</code>
+ or use the implementation constructors directly, e.g. <code>new TTestImpl()</code>.
+ </p>
+ <p>
+ <strong>Implementation Notes</strong>
+ <ul>
+ <li>Both one- and two-sample t-tests are supported. Two sample tests
+ can be either paired or unpaired and the unpaired two-sample tests can
+ be conducted under the assumption of equal subpopulation variances or
+ without this assumption. When equal variances is assumed, a pooled
+ variance estimate is used to compute the t-statistic and the degrees
+ of freedom used in the t-test equals the sum of the sample sizes minus 2.
+ When equal variances is not assumed, the t-statistic uses both sample
+ variances and the
+ <a href="http://www.itl.nist.gov/div898/handbook/prc/section3/gifs/nu3.gif">
+ Welch-Satterwaite approximation</a> is used to compute the degrees
+ of freedom. Methods to return t-statistics and p-values are provided in each
+ case, as well as boolean-valued methods to perform fixed significance
+ level tests. The names of methods or methods that assume equal
+ subpopulation variances always start with "homoscedastic." Test or
+ test-statistic methods that just start with "t" do not assume equal
+ variances. See the examples below and the API documentation for
+ more details.</li>
+ <li>The validity of the p-values returned by the t-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></li>
+ <li>p-values returned by t-, chi-square and Anova tests are exact, based
+ on numerical approximations to the t-, chi-square and F distributions in the
+ <code>distributions</code> package. </li>
+ <li>p-values returned by t-tests are for two-sided tests and the boolean-valued
+ methods supporting fixed significance level tests assume that the hypotheses
+ are two-sided. One sided tests can be performed by dividing returned p-values
+ (resp. critical values) by 2.</li>
+ <li>Degrees of freedom for chi-square tests are integral values, based on the
+ number of observed or expected counts (number of observed counts - 1)
+ for the goodness-of-fit tests and (number of columns -1) * (number of rows - 1)
+ for independence tests.</li>
+ </ul>
+ </p>
+ <p>
+ <strong>Examples:</strong>
+ <dl>
+ <dt><strong>One-sample <code>t</code> tests</strong></dt>
+ <br></br>
+ <dd>To compare the mean of a double[] array to a fixed value:
+ <source>
+double[] observed = {1d, 2d, 3d};
+double mu = 2.5d;
+System.out.println(TestUtils.t(mu, observed));
+ </source>
+ The code above will display the t-statisitic associated with a one-sample
+ t-test comparing the mean of the <code>observed</code> values against
+ <code>mu.</code>
+ </dd>
+ <dd>To compare the mean of a dataset described by a
+ <a href="../apidocs/org/apache/commons/math/stat/descriptive/StatisticalSummary.html">
+ StatisticalSummary</a> to a fixed value:
+ <source>
+double[] observed ={1d, 2d, 3d};
+double mu = 2.5d;
+SummaryStatistics sampleStats = new SummaryStatistics();
+for (int i = 0; i < observed.length; i++) {
+ sampleStats.addValue(observed[i]);
+}
+System.out.println(TestUtils.t(mu, observed));
+</source>
+ </dd>
+ <dd>To compute the p-value associated with the null hypothesis that the mean
+ of a set of values equals a point estimate, against the two-sided alternative that
+ the mean is different from the target value:
+ <source>
+double[] observed = {1d, 2d, 3d};
+double mu = 2.5d;
+System.out.println(TestUtils.tTest(mu, observed));
+ </source>
+ The snippet above will display the p-value associated with the null
+ hypothesis that the mean of the population from which the
+ <code>observed</code> values are drawn equals <code>mu.</code>
+ </dd>
+ <dd>To perform the test using a fixed significance level, use:
+ <source>
+TestUtils.tTest(mu, observed, alpha);
+ </source>
+ where <code>0 < alpha < 0.5</code> is the significance level of
+ the test. The boolean value returned will be <code>true</code> iff the
+ null hypothesis can be rejected with confidence <code>1 - alpha</code>.
+ To test, for example at the 95% level of confidence, use
+ <code>alpha = 0.05</code>
+ </dd>
+ <br></br>
+ <dt><strong>Two-Sample t-tests</strong></dt>
+ <br></br>
+ <dd><strong>Example 1:</strong> Paired test evaluating
+ the null hypothesis that the mean difference between corresponding
+ (paired) elements of the <code>double[]</code> arrays
+ <code>sample1</code> and <code>sample2</code> is zero.
+ <p>
+ To compute the t-statistic:
+ <source>
+TestUtils.pairedT(sample1, sample2);
+ </source>
+ </p>
+ <p>
+ To compute the p-value:
+ <source>
+TestUtils.pairedTTest(sample1, sample2);
+ </source>
+ </p>
+ <p>
+ To perform a fixed significance level test with alpha = .05:
+ <source>
+TestUtils.pairedTTest(sample1, sample2, .05);
+ </source>
+ </p>
+ The last example will return <code>true</code> iff the p-value
+ returned by <code>TestUtils.pairedTTest(sample1, sample2)</code>
+ is less than <code>.05</code>
+ </dd>
+ <dd><strong>Example 2: </strong> unpaired, two-sided, two-sample t-test using
+ <code>StatisticalSummary</code> instances, without assuming that
+ subpopulation variances are equal.
+ <p>
+ First create the <code>StatisticalSummary</code> instances. Both
+ <code>DescriptiveStatistics</code> and <code>SummaryStatistics</code>
+ implement this interface. Assume that <code>summary1</code> and
+ <code>summary2</code> are <code>SummaryStatistics</code> instances,
+ each of which has had at least 2 values added to the (virtual) dataset that
+ it describes. The sample sizes do not have to be the same -- all that is required
+ is that both samples have at least 2 elements.
+ </p>
+ <p><strong>Note:</strong> The <code>SummaryStatistics</code> class does
+ not store the dataset that it describes in memory, but it does compute all
+ statistics necessary to perform t-tests, so this method can be used to
+ conduct t-tests with very large samples. One-sample tests can also be
+ performed this way.
+ (See <a href="#1.2 Descriptive statistics">Descriptive statistics</a> for details
+ on the <code>SummaryStatistics</code> class.)
+ </p>
+ <p>
+ To compute the t-statistic:
+ <source>
+TestUtils.t(summary1, summary2);
+ </source>
+ </p>
+ <p>
+ To compute the p-value:
+ <source>
+TestUtils.tTest(sample1, sample2);
+ </source>
+ </p>
+ <p>
+ To perform a fixed significance level test with alpha = .05:
+ <source>
+TestUtils.tTest(sample1, sample2, .05);
+ </source>
+ </p>
+ <p>
+ In each case above, the test does not assume that the subpopulation
+ variances are equal. To perform the tests under this assumption,
+ replace "t" at the beginning of the method name with "homoscedasticT"
+ </p>
+ </dd>
+ <br></br>
+ <dt><strong>Chi-square tests</strong></dt>
+ <br></br>
+ <dd>To compute a chi-square statistic measuring the agreement between a
+ <code>long[]</code> array of observed counts and a <code>double[]</code>
+ array of expected counts, use:
+ <source>
+long[] observed = {10, 9, 11};
+double[] expected = {10.1, 9.8, 10.3};
+System.out.println(TestUtils.chiSquare(expected, observed));
+ </source>
+ the value displayed will be
+ <code>sum((expected[i] - observed[i])^2 / expected[i])</code>
+ </dd>
+ <dd> To get the p-value associated with the null hypothesis that
+ <code>observed</code> conforms to <code>expected</code> use:
+ <source>
+TestUtils.chiSquareTest(expected, observed);
+ </source>
+ </dd>
+ <dd> To test the null hypothesis that <code>observed</code> conforms to
+ <code>expected</code> with <code>alpha</code> siginficance level
+ (equiv. <code>100 * (1-alpha)%</code> confidence) where <code>
+ 0 < alpha < 1 </code> use:
+ <source>
+TestUtils.chiSquareTest(expected, observed, alpha);
+ </source>
+ The boolean value returned will be <code>true</code> iff the null hypothesis
+ can be rejected with confidence <code>1 - alpha</code>.
+ </dd>
+ <dd>To compute a chi-square statistic 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 a two-dimensional (long[][])
+ <code>counts</code> array viewed as a two-way table, use:
+ <source>
+TestUtils.chiSquareTest(counts);
+ </source>
+ The rows of the 2-way table are
+ <code>count[0], ... , count[count.length - 1]. </code><br></br>
+ The chi-square statistic returned is
+ <code>sum((counts[i][j] - expected[i][j])^2/expected[i][j])</code>
+ where the sum is taken over all table entries and
+ <code>expected[i][j]</code> is the product of the row and column sums at
+ row <code>i</code>, column <code>j</code> divided by the total count.
+ </dd>
+ <dd>To compute the p-value associated with the null hypothesis that
+ the classifications represented by the counts in the columns of the input 2-way
+ table are independent of the rows, use:
+ <source>
+ TestUtils.chiSquareTest(counts);
+ </source>
+ </dd>
+ <dd>To perform a chi-square test of independence with <code>alpha</code>
+ siginficance level (equiv. <code>100 * (1-alpha)%</code> confidence)
+ where <code>0 < alpha < 1 </code> use:
+ <source>
+TestUtils.chiSquareTest(counts, alpha);
+ </source>
+ The boolean value returned will be <code>true</code> iff the null
+ hypothesis can be rejected with confidence <code>1 - alpha</code>.
+ </dd>
+ <br></br>
+ <dt><strong>One-Way Anova tests</strong></dt>
+ <br></br>
+ <dd>To conduct a One-Way Analysis of Variance (ANOVA) to evaluate the
+ null hypothesis that the means of a collection of univariate datasets
+ are the same, start by loading the datasets into a collection, e.g.
+ <source>
+double[] classA =
+ {93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0 };
+double[] classB =
+ {99.0, 92.0, 102.0, 100.0, 102.0, 89.0 };
+double[] classC =
+ {110.0, 115.0, 111.0, 117.0, 128.0, 117.0 };
+List classes = new ArrayList();
+classes.add(classA);
+classes.add(classB);
+classes.add(classC);
+ </source>
+ Then you can compute ANOVA F- or p-values associated with the
+ null hypothesis that the class means are all the same
+ using a <code>OneWayAnova</code> instance or <code>TestUtils</code>
+ methods:
+ <source>
+double fStatistic = TestUtils.oneWayAnovaFValue(classes); // F-value
+double pValue = TestUtils.oneWayAnovaPValue(classes); // P-value
+ </source>
+ To test perform a One-Way Anova test with signficance level set at 0.01
+ (so the test will, assuming assumptions are met, reject the null
+ hypothesis incorrectly only about one in 100 times), use
+ <source>
+TestUtils.oneWayAnovaTest(classes, 0.01); // returns a boolean
+ // true means reject null hypothesis
+ </source>
+ </dd>
+ </dl>
+ </p>
+ </subsection>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/transform.xml b/src/site/xdoc/userguide/transform.xml
new file mode 100644
index 0000000..3d64ef2
--- /dev/null
+++ b/src/site/xdoc/userguide/transform.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 937893 $ $Date: 2010-04-25 23:42:47 +0200 (dim. 25 avril 2010) $ -->
+<document url="transform.html">
+
+ <properties>
+ <title>The Commons Math User Guide - Transform methods</title>
+ </properties>
+
+ <body>
+ <section name="10 Transform methods">
+ <p>
+ This package provides a few transformers for signal analysis. All transformers
+ provide both direct and inverse transforms.
+ <ul>
+ <li><a href="../apidocs/org/apache/commons/math/transform/FastFourierTransformer.html">
+ FastFourierTransformer</a> (produces <code>Complex</code> results)</li>
+ <li><a href="../apidocs/org/apache/commons/math/transform/FastCosineTransformer.html">
+ FastCosineTransformer</a> (produces real results)</li>
+ <li><a href="../apidocs/org/apache/commons/math/transform/FastSineTransformer.html">
+ FastSineTransformer</a> (produces real results)</li>
+ <li><a href="../apidocs/org/apache/commons/math/transform/FastHadamardTransformer.html">
+ FastHadamardTransformer</a> (produces real results)</li>
+ </ul>
+ </p>
+ </section>
+ </body>
+</document>
diff --git a/src/site/xdoc/userguide/utilities.xml b/src/site/xdoc/userguide/utilities.xml
new file mode 100644
index 0000000..02b4234
--- /dev/null
+++ b/src/site/xdoc/userguide/utilities.xml
@@ -0,0 +1,195 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<?xml-stylesheet type="text/xsl" href="./xdoc.xsl"?>
+<!-- $Revision: 958552 $ $Date: 2010-06-28 14:09:02 +0200 (lun. 28 juin 2010) $ -->
+<document url="utilities.html">
+
+<properties>
+ <title>The Commons Math User Guide - Utilites</title>
+</properties>
+
+<body>
+
+<section name="6 Utilities">
+
+<subsection name="6.1 Overview" href="overview">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/util/package-summary.html">
+ org.apache.commons.math.util</a> package collects a group of array utilities,
+ value transformers, and numerical routines used by implementation classes in
+ commons-math.
+ </p>
+</subsection>
+
+<subsection name="6.2 Double array utilities" href="arrays">
+ <p>
+ To maintain statistics based on a "rolling" window of values, a resizable
+ array implementation was developed and is provided for reuse in the
+ <code>util</code> package. The core functionality provided is described in
+ the documentation for the interface,
+ <a href="../apidocs/org/apache/commons/math/util/DoubleArray.html">
+ DoubleArray</a>. This interface adds one method,
+ <code>addElementRolling(double)</code> to basic list accessors.
+ The <code>addElementRolling</code> method adds an element
+ (the actual parameter) to the end of the list and removes the first element
+ in the list.
+ </p>
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/util/ResizableDoubleArray.html">
+ ResizableDoubleArray</a> class provides a configurable, array-backed
+ implementation of the <code>DoubleArray</code> interface.
+ When <code>addElementRolling</code> is invoked, the underlying
+ array is expanded if necessary, the new element is added to the end of the
+ array and the "usable window" of the array is moved forward, so that
+ the first element is effectively discarded, what was the second becomes the
+ first, and so on. To efficiently manage storage, two maintenance
+ operations need to be periodically performed -- orphaned elements at the
+ beginning of the array need to be reclaimed and space for new elements at
+ the end needs to be created. Both of these operations are handled
+ automatically, with frequency / effect driven by the configuration
+ properties <code>expansionMode</code>, <code>expansionFactor</code> and
+ <code>contractionCriteria.</code> See
+ <a href="../apidocs/org/apache/commons/math/util/ResizableDoubleArray.html">
+ ResizableDoubleArray</a>
+ for details.
+ </p>
+</subsection>
+
+<subsection name="6.3 int/double hash map" href="int_double_hash_map">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/util/OpenIntToDoubleHashMap.html">
+ OpenIntToDoubleHashMap</a> class provides a specialized hash map
+ implementation for int/double. This implementation has a much smaller memory
+ overhead than standard <code>java.util.HashMap</code> class. It uses open addressing
+ and primitive arrays, which greatly reduces the number of intermediate objects and
+ improve data locality.
+ </p>
+</subsection>
+
+<subsection name="6.4 Continued Fractions" href="continued_fractions">
+ <p>
+ The <a href="../apidocs/org/apache/commons/math/util/ContinuedFraction.html">
+ ContinuedFraction</a> class provides a generic way to create and evaluate
+ continued fractions. The easiest way to create a continued fraction is
+ to subclass <code>ContinuedFraction</code> and override the
+ <code>getA</code> and <code>getB</code> methods which return
+ the continued fraction terms. The precise definition of these terms is
+ explained in <a href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ Continued Fraction, equation (1)</a> from MathWorld.
+ </p>
+ <p>
+ As an example, the constant Pi can be computed using a <a href="http://functions.wolfram.com/Constants/Pi/10/0002/">continued fraction</a>. The following anonymous class
+ provides the implementation:
+ <source>ContinuedFraction c = new ContinuedFraction() {
+ public double getA(int n, double x) {
+ switch(n) {
+ case 0: return 3.0;
+ default: return 6.0;
+ }
+ }
+
+ public double getB(int n, double x) {
+ double y = (2.0 * n) - 1.0;
+ return y * y;
+ }
+}</source>
+ </p>
+ <p>
+ Then, to evalute Pi, simply call any of the <code>evalute</code> methods
+ (Note, the point of evalution in this example is meaningless since Pi is a
+ constant).
+ </p>
+ <p>
+ For a more practical use of continued fractions, consider the <a href="http://functions.wolfram.com/ElementaryFunctions/Exp/10/">exponential function</a>.
+ The following anonymous class provides its implementation:
+ <source>ContinuedFraction c = new ContinuedFraction() {
+ public double getA(int n, double x) {
+ if (n % 2 == 0) {
+ switch(n) {
+ case 0: return 1.0;
+ default: return 2.0;
+ }
+ } else {
+ return n;
+ }
+ }
+
+ public double getB(int n, double x) {
+ if (n % 2 == 0) {
+ return -x;
+ } else {
+ return x;
+ }
+ }
+}</source>
+ </p>
+ <p>
+ Then, to evalute <i>e</i><sup>x</sup> for any value x, simply call any of the
+ <code>evalute</code> methods.
+ </p>
+</subsection>
+
+<subsection name="6.5 binomial coefficients, factorials and other common math functions" href="math_utils">
+ <p>
+ A collection of reusable math functions is provided in the
+ <a href="../apidocs/org/apache/commons/math/util/MathUtils.html">MathUtils</a>
+ utility class. MathUtils currently includes methods to compute the following: <ul>
+ <li>
+ Binomial coefficients -- "n choose k" available as an (exact) long value,
+ <code>binomialCoefficient(int, int)</code> for small n, k; as a double,
+ <code>binomialCoefficientDouble(int, int)</code> for larger values; and in
+ a "super-sized" version, <code>binomialCoefficientLog(int, int)</code>
+ that returns the natural logarithm of the value.</li>
+ <li>
+ Factorials -- like binomial coefficients, these are available as exact long
+ values, <code>factorial(int)</code>; doubles,
+ <code>factorialDouble(int)</code>; or logs, <code>factorialLog(int)</code>. </li>
+ <li>
+ Hyperbolic sine and cosine functions --
+ <code>cosh(double), sinh(double)</code></li>
+ <li>
+ sign (+1 if argument > 0, 0 if x = 0, and -1 if x < 0) and
+ indicator (+1.0 if argument >= 0 and -1.0 if argument < 0) functions
+ for variables of all primitive numeric types.</li>
+ <li>
+ a hash function, <code>hash(double),</code> returning a long-valued
+ hash code for a double value.
+ </li>
+ <li>
+ Convience methods to round floating-point number to arbitrary precision.
+ </li>
+ <li>
+ Least common multiple and greatest common denominator functions.
+ </li>
+ </ul>
+ </p>
+</subsection>
+
+<subsection name="6.7 Miscellaneous" href="math_utils">
+ The <a href="../apidocs/org/apache/commons/math/util/MultidimensionalCounter.html">
+ MultidimensionalCounter</a> is a utility class that converts a set of indices
+ (identifying points in a multidimensional space) to a single index (e.g. identifying
+ a location in a one-dimensional array.
+</subsection>
+
+</section>
+
+</body>
+</document>
diff --git a/src/site/xdoc/userguide/xdoc.xsl b/src/site/xdoc/userguide/xdoc.xsl
new file mode 100644
index 0000000..cfbd5f5
--- /dev/null
+++ b/src/site/xdoc/userguide/xdoc.xsl
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="/">
+ <html xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <head>
+ <xsl:apply-templates select="/document/properties/title"/>
+ <xsl:apply-templates select="/document/meta"/>
+ <style type="text/css">
+ @import url("../style/tigris.css");
+ @import url("../style/maven.css");
+ @import url("../style/project.css");
+ </style>
+ <link rel="stylesheet" href="../style/print.css" type="text/css" media="print"></link>
+ </head>
+ <xsl:apply-templates select="/document/body"/>
+ </html>
+ </xsl:template>
+ <xsl:template match="body">
+ <body>
+ <div class="app">
+ <xsl:apply-templates/>
+ </div>
+ </body>
+ </xsl:template>
+ <xsl:template match="section">
+ <div>
+ <h3><xsl:value-of select="@name"/></h3>
+ <xsl:apply-templates/>
+ </div>
+ </xsl:template>
+ <xsl:template match="subsection">
+ <div>
+ <h4><xsl:value-of select="@name"/></h4>
+ <xsl:apply-templates/>
+ </div>
+ </xsl:template>
+ <xsl:template match="source">
+ <div id="source">
+ <pre>
+ <xsl:apply-templates/>
+ </pre>
+ </div>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="@*"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/src/test/R/ChiSquareDistributionTestCases.R b/src/test/R/ChiSquareDistributionTestCases.R
new file mode 100644
index 0000000..78929ef
--- /dev/null
+++ b/src/test/R/ChiSquareDistributionTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate ChiSquare distribution tests in
+# org.apache.commons.math.distribution.ChiSquareDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+verifyDistribution <- function(points, expected, df, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pchisq(point, df, log = FALSE)
+ }
+ output <- c("Distribution test df = ", df)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, df, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dchisq(point, df, log = FALSE)
+ }
+ output <- c("Density test df = ", df)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, df, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qchisq(point, df, log = FALSE)
+ }
+ output <- c("Quantile test df = ", df)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("ChiSquare Distribution test cases\n")
+
+df <- 5
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.0115379817652, 0.0415948507811, 0.0665060119842, 0.0919455953114, 0.121472591024,
+ 0.000433630076361, 0.00412780610309, 0.00999340341045, 0.0193246438937, 0.0368460089216)
+distributionPoints <- c(0.210212602629, 0.554298076728, 0.831211613487, 1.14547622606, 1.61030798696,
+ 20.5150056524, 15.0862724694, 12.8325019940, 11.0704976935, 9.23635689978)
+verifyQuantiles(distributionValues, distributionPoints, df, tol)
+verifyDistribution(distributionPoints, distributionValues, df, tol)
+verifyDensity(distributionPoints, densityValues, df, tol)
+
+df <- .1
+distributionPoints <- c(1.16892641146e-60, 1.16892641146e-40, 1.06313237798e-32, 1.11477509638e-26, 1.16892641146e-20,
+ 5.47291719746, 2.17525480018, 1.13434752351, 0.531864604852, 0.152634227818)
+verifyQuantiles(distributionValues, distributionPoints, df, tol)
+verifyDistribution(distributionPoints, distributionValues, df, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/FDistributionTestCases.R b/src/test/R/FDistributionTestCases.R
new file mode 100644
index 0000000..e5f3656
--- /dev/null
+++ b/src/test/R/FDistributionTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate F distribution tests in
+# org.apache.commons.math.distribution.TDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+verifyDistribution <- function(points, expected, numeratorDf, denominatorDf, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pf(point, numeratorDf, denominatorDf, log = FALSE)
+ }
+ output <- c("Distribution test numerator df = ", numeratorDf, " denominator df = ", denominatorDf)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, numeratorDf, denominatorDf, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- df(point, numeratorDf, denominatorDf, log = FALSE)
+ }
+ output <- c("Density test numerator df = ", numeratorDf, " denominator df = ", denominatorDf)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, numeratorDf, denominatorDf, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qf(point, numeratorDf, denominatorDf, log = FALSE)
+ }
+ output <- c("Quantile test numerator df = ", numeratorDf, " denominator df = ", denominatorDf)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("F Distribution test cases\n")
+
+numeratorDf <- 5
+denominatorDf <- 6
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.0689156576706, 0.236735653193, 0.364074131941, 0.481570789649, 0.595880479994,
+ 0.000133443915657, 0.00286681303403, 0.00969192007502, 0.0242883861471, 0.0605491314658)
+distributionPoints <- c(0.0346808448626, 0.0937009113303, 0.143313661184, 0.202008445998, 0.293728320107,
+ 20.8026639595, 8.74589525602, 5.98756512605, 4.38737418741, 3.10751166664)
+verifyQuantiles(distributionValues, distributionPoints, numeratorDf, denominatorDf, tol)
+verifyDistribution(distributionPoints, distributionValues, numeratorDf, denominatorDf, tol)
+verifyDensity(distributionPoints, densityValues, numeratorDf, denominatorDf, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/GammaDistributionTestCases.R b/src/test/R/GammaDistributionTestCases.R
new file mode 100644
index 0000000..7cbefda
--- /dev/null
+++ b/src/test/R/GammaDistributionTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Gamma distribution tests in
+# org.apache.commons.math.distribution.GammaDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+verifyDistribution <- function(points, expected, alpha, beta, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pgamma(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Distribution test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, alpha, beta, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dgamma(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Density test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, alpha, beta, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qgamma(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Quantile test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Gamma Distribution test cases\n")
+
+shape <- 4
+scale <- 2
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.00427280075546, 0.0204117166709, 0.0362756163658, 0.0542113174239, 0.0773195272491,
+ 0.000394468852816, 0.00366559696761, 0.00874649473311, 0.0166712508128, 0.0311798227954)
+distributionPoints <- c(0.857104827257, 1.64649737269, 2.17973074725, 2.7326367935, 3.48953912565,
+ 26.1244815584, 20.0902350297, 17.5345461395, 15.5073130559, 13.3615661365)
+verifyQuantiles(distributionValues, distributionPoints, shape, scale, tol)
+verifyDistribution(distributionPoints, distributionValues, shape, scale, tol)
+verifyDensity(distributionPoints, densityValues, shape, scale, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/README.txt b/src/test/R/README.txt
new file mode 100644
index 0000000..c6549bc
--- /dev/null
+++ b/src/test/R/README.txt
@@ -0,0 +1,168 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#------------------------------------------------------------------------------
+
+INTRODUCTION
+
+The purpose of the R programs included in this directory is to validate
+the target values used in Apache commons math unit tests. Success running the
+R and commons-math tests on a platform (OS and R version) means that R and
+commons-math give results for the test cases that are close in value. The
+tests include configurable tolerance levels; but care must be taken in changing
+these, since in most cases the pre-set tolerance is close to the number of
+decimal digits used in expressing the expected values (both here and in the
+corresponding commons-math unit tests).
+
+Of course it is always possible that both R and commons-math give incorrect
+values for test cases, so these tests should not be interpreted as definitive
+in any absolute sense. The value of developing and running the tests is really
+to generate questions (and answers!) when the two systems give different
+results.
+
+Contributions of additional test cases (both R and Junit code) or just
+R programs to validate commons-math tests that are not covered here would be
+greatly appreciated.
+
+SETUP
+
+0) Download and install R. You can get R here
+http://www.r-project.org/
+Follow the install instructions and make sure that you can launch R from this
+(i.e., either explitly add R to your OS path or let the install package do it
+for you).
+
+1) Launch R from this directory and type
+> source("testAll")
+to an R prompt. This should produce output to the console similar to this:
+
+Binomial test cases
+Density test n = 10, p = 0.7...........................................SUCCEEDED
+Distribution test n = 10, p = 0.7......................................SUCCEEDED
+Inverse Distribution test n = 10, p = 0.7..............................SUCCEEDED
+Density test n = 5, p = 0..............................................SUCCEEDED
+Distribution test n = 5, p = 0.........................................SUCCEEDED
+Density test n = 5, p = 1..............................................SUCCEEDED
+Distribution test n = 5, p = 1.........................................SUCCEEDED
+--------------------------------------------------------------------------------
+Normal test cases
+Distribution test mu = 2.1, sigma = 1.4................................SUCCEEDED
+Distribution test mu = 2.1, sigma = 1.4................................SUCCEEDED
+Distribution test mu = 0, sigma = 1....................................SUCCEEDED
+Distribution test mu = 0, sigma = 0.1..................................SUCCEEDED
+--------------------------------------------------------------------------------
+...
+<more test reports>
+
+
+WORKING WITH THE TESTS
+
+The R distribution comes with online manuals that you can view by launching
+a browser instance and then entering
+
+> help.start()
+
+at an R prompt. Poking about in the test case files and the online docs should
+bring you up to speed fairly quickly. Here are some basic things to get
+you started. I should note at this point that I am by no means an expert R
+programmer, so some things may not be implemented in the the nicest way.
+Comments / suggestions for improvement are welcome!
+
+All of the test cases use some basic functions and global constants (screen
+width and success / failure strings) defined in "testFunctions." The
+R "source" function is used to "import" these functions into each of the test
+programs. The "testAll" program pulls together and executes all of the
+individual test programs. You can execute any one of them by just entering
+
+> source(<program-name>).
+
+The "assertEquals" function in the testFunctions file mimics the similarly
+named function used by Junit:
+
+assertEquals <- function(expected, observed, tol, message) {
+ if(any(abs(expected - observed) > tol)) {
+ cat("FAILURE: ",message,"\n")
+ cat("EXPECTED: ",expected,"\n")
+ cat("OBSERVED: ",observed,"\n")
+ return(0)
+ } else {
+ return(1)
+ }
+}
+
+The <expected> and <observed> arguments can be scalar values, vectors or
+matrices. If the arguments are vectors or matrices, corresponding entries
+are compared.
+
+The standard pattern used throughout the tests looks like this (from
+binomialTestCases):
+
+Start by defining a "verification function" -- in this example a function to
+verify computation of binomial probabilities. The <points> argument is a vector
+of integer values to feed into the density function, <expected> is a vector of
+the computed probabilies from the commons-math Junit tests, <n> and <p> are
+parameters of the distribution and <tol> is the error tolerance of the test.
+The function computes the probabilities using R and compares the values that
+R produces with those in the <expected> vector.
+
+verifyDensity <- function(points, expected, n, p, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dbinom(point, n, p, log = FALSE)
+ }
+ output <- c("Density test n = ", n, ", p = ", p)
+ if (assertEquals(expected,rDensityValues,tol,"Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+The displayPadded function just displays its first and second arguments with
+enough dots in between to make the whole string WIDTH characters long. It is
+defined in testFunctions.
+
+Then call this function with different parameters corresponding to the different
+Junit test cases:
+
+size <- 10.0
+probability <- 0.70
+
+densityPoints <- c(-1,0,1,2,3,4,5,6,7,8,9,10,11)
+densityValues <- c(0, 0.0000, 0.0001, 0.0014, 0.0090, 0.0368, 0.1029,
+ 0.2001, 0.2668, 0.2335, 0.1211, 0.0282, 0)
+...
+verifyDensity(densityPoints, densityValues, size, probability, tol)
+
+If the values computed by R match the target values in densityValues, this will
+produce one line of output to the console:
+
+Density test n = 10, p = 0.7...........................................SUCCEEDED
+
+If you modify the value of tol set at the top of binomialTestCases to make the
+test more sensitive than the number of digits specified in the densityValues
+vector, it will fail, producing the following output, showing the failure and
+the expected and observed values:
+
+FAILURE: Density Values
+EXPECTED: 0 0 1e-04 0.0014 0.009 0.0368 0.1029 0.2001 0.2668 0.2335 0.1211 /
+ 0.0282 0
+OBSERVED: 0 5.9049e-06 0.000137781 0.0014467005 0.009001692 0.036756909 /
+0.1029193452 0.200120949 0.266827932 0.2334744405 0.121060821 0.0282475249 0
+Density test n = 10, p = 0.7..............................................FAILED
+
+
diff --git a/src/test/R/TDistributionTestCases.R b/src/test/R/TDistributionTestCases.R
new file mode 100644
index 0000000..723e958
--- /dev/null
+++ b/src/test/R/TDistributionTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate T distribution tests in
+# org.apache.commons.math.distribution.TDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+verifyDistribution <- function(points, expected, df, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pt(point, df, log = FALSE)
+ }
+ output <- c("Distribution test df = ", df)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, df, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dt(point, df, log = FALSE)
+ }
+ output <- c("Density test df = ", df)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, df, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qt(point, df, log = FALSE)
+ }
+ output <- c("Quantile test df = ", df)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("T Distribution test cases\n")
+
+df <- 5
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.000756494565517, 0.0109109752919, 0.0303377878006, 0.0637967988952, 0.128289492005,
+ 0.000756494565517, 0.0109109752919, 0.0303377878006, 0.0637967988952, 0.128289492005)
+distributionPoints <- c(-5.89342953136, -3.36492999891, -2.57058183564, -2.01504837333, -1.47588404882,
+ 5.89342953136, 3.36492999891, 2.57058183564, 2.01504837333, 1.47588404882)
+verifyQuantiles(distributionValues, distributionPoints, df, tol)
+verifyDistribution(distributionPoints, distributionValues, df, tol)
+verifyDensity(distributionPoints, densityValues, df, tol)
+
+df <- 1
+densityValues <- c(3.14158231817e-06, 0.000314055924703, 0.00195946145194, 0.00778959736375, 0.0303958893917,
+ 3.14158231817e-06, 0.000314055924703, 0.00195946145194, 0.00778959736375, 0.0303958893917)
+distributionPoints <- c(-318.308838986, -31.8205159538, -12.7062047362, -6.31375151468, -3.07768353718,
+ 318.308838986, 31.8205159538, 12.7062047362, 6.31375151468, 3.07768353718)
+verifyQuantiles(distributionValues, distributionPoints, df, tol)
+verifyDistribution(distributionPoints, distributionValues, df, tol)
+verifyDensity(distributionPoints, densityValues, df, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/TTestCases b/src/test/R/TTestCases
new file mode 100644
index 0000000..6aa5e2d
--- /dev/null
+++ b/src/test/R/TTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate TTest tests in
+# org.apache.commons.math.inference.TTestImpl
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"),
+# mu = 0, paired = FALSE, var.equal = FALSE, ... )
+# Arguments
+# x a numeric vector of data values.
+# y an optional numeric vector data values.
+# alternative a character string specifying the alternative hypothesis,
+# must be one of "two.sided" (default), "greater" or "less". You can specify
+# just the initial letter.
+# mu a number indicating the true value of the mean (or difference in means
+# if you are performing a two sample test).
+# paired a logical indicating whether you want a paired t-test.
+# var.equal a logical variable indicating whether to treat the two
+# variances as being equal.
+# If TRUE then the pooled variance is used to estimate the variance,
+# otherwise the Welch (or Satterthwaite) approximation to the degrees
+# of freedom is used.
+#------------------------------------------------------------------------------
+tol <- 1E-10 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+#------------------------------------------------------------------------------
+source("testFunctions") # utility test functions
+#------------------------------------------------------------------------------
+# Verification function
+#
+verifyTest <- function(out,expectedP, expectedT,
+ tol) {
+ if (assertEquals(expectedP, out$p.value, tol,
+ "Ttest p value")) {
+ displayPadded(output, SUCCEEDED, 80)
+ } else {
+ displayPadded(output, FAILED, 80)
+ }
+ output <- c("t test test statistic")
+ if (assertEquals(expectedT, out$statistic, tol,
+ "Ttest t statistic")) {
+ displayPadded(output, SUCCEEDED, 80)
+ } else {
+ displayPadded(output, FAILED, 80)
+ }
+ displayDashes(WIDTH)
+}
+
+cat("One-sample, two-sided TTest test cases \n")
+sample1 <- c(93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0, 88.0,
+ 98.0, 94.0, 101.0, 92.0, 95.0)
+out <- t.test(sample1, mu=100.0)
+expectedP <- 0.0136390585873
+expectedT<- -2.81976445346
+verifyTest(out,expectedP, expectedT, tol)
+
+cat("One-sample, one-sided TTest test cases \n")
+sample1 <- c(2, 0, 6, 6, 3, 3, 2, 3, -6, 6, 6, 6, 3, 0, 1, 1, 0, 2, 3, 3)
+out <- t.test(sample1, mu=0.0, alternative="g")
+expectedP <- 0.000521637019637
+expectedT<- 3.86485535541
+verifyTest(out,expectedP, expectedT, tol)
+
+cat("Homoscedastic TTest test cases \n")
+sample1 <- c(2, 4, 6, 8, 10, 97)
+sample2 <- c(4, 6, 8, 10, 16)
+out <- t.test(sample1,sample2,var.equal = TRUE)
+expectedP <- 0.4833963785
+expectedT<- 0.73096310086
+verifyTest(out,expectedP, expectedT, tol)
+
+cat("Heteroscedastic TTest test cases \n")
+sample1 <- c(7, -4, 18, 17, -3, -5, 1, 10, 11, -2)
+sample2 <- c(-1, 12, -1, -3, 3, -5, 5, 2, -11, -1, -3)
+out <- t.test(sample1,sample2,var.equal = FALSE)
+expectedP <- 0.128839369622
+expectedT<- 1.60371728768
+verifyTest(out,expectedP, expectedT, tol)
+
+cat("Small sample, heteroscedastic test cases \n")
+sample1 <- c(1,3)
+sample2 <- c(4,5)
+out <- t.test(sample1,sample2,var.equal = FALSE)
+expectedP <- 0.198727388935
+expectedT<- -2.2360679775
+verifyTest(out,expectedP, expectedT, tol)
+
diff --git a/src/test/R/WeibullDistributionTestCases.R b/src/test/R/WeibullDistributionTestCases.R
new file mode 100644
index 0000000..fadc625
--- /dev/null
+++ b/src/test/R/WeibullDistributionTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Weibull distribution tests in
+# org.apache.commons.math.distribution.GammaDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+verifyDistribution <- function(points, expected, alpha, beta, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pweibull(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Distribution test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, alpha, beta, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dweibull(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Density test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, alpha, beta, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qweibull(point, shape=alpha, scale=beta, log = FALSE)
+ }
+ output <- c("Quantile test shape = ", shape, " scale = ", scale)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Weibull Distribution test cases\n")
+
+shape <- 1.2
+scale <- 2.1
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.180535929306, 0.262801138133, 0.301905425199, 0.330899152971, 0.353441418887, 0.000788590320203,
+ 0.00737060094841, 0.0177576041516, 0.0343043442574, 0.065664589369)
+distributionPoints <- c(0.00664355180993, 0.0454328283309, 0.0981162737374, 0.176713524579, 0.321946865392,
+ 10.5115496887, 7.4976304671, 6.23205600701, 5.23968436955, 4.20790282578)
+verifyQuantiles(distributionValues, distributionPoints, shape, scale, tol)
+verifyDistribution(distributionPoints, distributionValues, shape, scale, tol)
+verifyDensity(distributionPoints, densityValues, shape, scale, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/anovaTestCases b/src/test/R/anovaTestCases
new file mode 100644
index 0000000..077ba09
--- /dev/null
+++ b/src/test/R/anovaTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Binomial distribution tests in
+# org.apache.commons.math.distribution.BinomialDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# anova(model) <- anova
+# lm(frame) <- linear model
+#------------------------------------------------------------------------------
+tol <- 1E-12 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+options(digits=16) # override number of digits displayed
+
+# function to verify anova computations
+
+verifyAnova <- function(frame, expectedP, expectedF, frameName) {
+ a <- anova(lm(frame))
+ p <- a$"Pr(>F)"[1]
+ f <- a$"F value"[1]
+ output <- c("P-value test frame = ", frameName)
+ if (assertEquals(expectedP,p,tol,"P value")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("F-value test frame = ", frameName)
+ if (assertEquals(expectedF,f,tol,"F value")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Anova test cases\n")
+classA <- c(93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0)
+classB <- c(99.0, 92.0, 102.0, 100.0, 102.0, 89.0)
+classC <- c(110.0, 115.0, 111.0, 117.0, 128.0, 117.0)
+
+threeClasses = data.frame(val = c(classA, classB, classC),
+class=c(rep("classA", length(classA)),
+ rep("classB", length(classB)),
+ rep("classC", length(classC))))
+
+verifyAnova(threeClasses,6.959446e-06, 24.67361709460624, "Three classes")
+
+twoClasses = data.frame(val = c(classA, classB),
+class=c(rep("classA", length(classA)), rep("classB", length(classB))))
+verifyAnova(twoClasses, 0.904212960464, 0.0150579150579, "Two classes")
+
+displayDashes(WIDTH)
\ No newline at end of file
diff --git a/src/test/R/binomialTestCases b/src/test/R/binomialTestCases
new file mode 100644
index 0000000..6b1871c
--- /dev/null
+++ b/src/test/R/binomialTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Binomial distribution tests in
+# org.apache.commons.math.distribution.BinomialDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# dbinom(x, size, prob, log = FALSE) <- density
+# pbinom(q, size, prob, lower.tail = TRUE, log.p = FALSE) <- distribution
+# qbinom(p, size, prob, lower.tail = TRUE, log.p = FALSE) <- quantiles
+#------------------------------------------------------------------------------
+tol <- 1E-4 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, n, p, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dbinom(point, n, p, log = FALSE)
+ }
+ output <- c("Density test n = ", n, ", p = ", p)
+ if (assertEquals(expected,rDensityValues,tol,"Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, n, p, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pbinom(point, n, p, log = FALSE)
+ }
+ output <- c("Distribution test n = ", n, ", p = ", p)
+ if (assertEquals(expected,rDistValues,tol,"Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Binomial test cases\n")
+
+size <- 10.0
+probability <- 0.70
+
+densityPoints <- c(-1,0,1,2,3,4,5,6,7,8,9,10,11)
+densityValues <- c(0, 0.0000, 0.0001, 0.0014, 0.0090, 0.0368, 0.1029,
+ 0.2001, 0.2668, 0.2335, 0.1211, 0.0282, 0)
+distributionValues <- c(0, 0.0000, 0.0001, 0.0016, 0.0106, 0.0473,
+ 0.1503, 0.3504, 0.6172, 0.8507, 0.9718, 1, 1)
+inverseCumPoints <- c( 0.001, 0.010, 0.025, 0.050, 0.100, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+inverseCumValues <- c(1, 2, 3, 4, 4, 9, 9, 9, 8, 8)
+
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+verifyDistribution(densityPoints, distributionValues, size, probability, tol)
+
+i <- 0
+rInverseCumValues <- rep(0,length(inverseCumPoints))
+for (point in inverseCumPoints) {
+ i <- i + 1
+ rInverseCumValues[i] <- qbinom(point, size, probability, log = FALSE)
+}
+
+output <- c("Inverse Distribution test n = ", size, ", p = ", probability)
+# R defines quantiles from the right, need to subtract one
+if (assertEquals(inverseCumValues, rInverseCumValues-1, tol,
+ "Inverse Dist Values")) {
+ displayPadded(output, SUCCEEDED, 80)
+} else {
+ displayPadded(output, FAILED, 80)
+}
+
+# Degenerate cases
+
+size <- 5
+probability <- 0.0
+
+densityPoints <- c(-1, 0, 1, 10, 11)
+densityValues <- c(0, 1, 0, 0, 0)
+distributionPoints <- c(-1, 0, 1, 5, 10)
+distributionValues <- c(0, 1, 1, 1, 1)
+
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+verifyDistribution(distributionPoints,distributionValues,size,probability,tol)
+
+size <- 5
+probability <- 1.0
+
+densityPoints <- c(-1, 0, 1, 2, 5, 10)
+densityValues <- c(0, 0, 0, 0, 1, 0)
+distributionPoints <- c(-1, 0, 1, 2, 5, 10)
+distributionValues <- c(0, 0, 0, 0, 1, 1)
+
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+verifyDistribution(distributionPoints,distributionValues,size,probability,tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/cauchyTestCases.R b/src/test/R/cauchyTestCases.R
new file mode 100644
index 0000000..b046ca5
--- /dev/null
+++ b/src/test/R/cauchyTestCases.R
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Cauchy distribution tests in
+# org.apache.commons.math.distribution.CauchyDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, median, scale, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pcauchy(point, median, scale, log = FALSE)
+ }
+ output <- c("Distribution test median = ",median,", scale = ", scale)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, median, scale, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dcauchy(point, median, scale, log = FALSE)
+ }
+ output <- c("Density test median = ",median,", scale = ", scale)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+
+verifyQuantiles <- function(points, expected, median, scale, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qcauchy(point, median, scale, log = FALSE)
+ }
+ output <- c("Quantile test median = ",median,", scale = ", scale)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Cauchy test cases\n")
+
+median <- 1.2
+scale <- 2.1
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(1.49599158008e-06, 0.000149550440335, 0.000933076881878, 0.00370933207799, 0.0144742330437,
+ 1.49599158008e-06, 0.000149550440335, 0.000933076881878, 0.00370933207799, 0.0144742330437)
+distributionPoints <- c(-667.24856187, -65.6230835029, -25.4830299460, -12.0588781808, -5.26313542807,
+ 669.64856187, 68.0230835029, 27.8830299460, 14.4588781808, 7.66313542807)
+verifyDistribution(distributionPoints, distributionValues, median, scale, tol)
+verifyDensity(distributionPoints, densityValues, median, scale, tol)
+verifyQuantiles(distributionValues, distributionPoints, median, scale, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/chiSquareTestCases b/src/test/R/chiSquareTestCases
new file mode 100644
index 0000000..747b5e8
--- /dev/null
+++ b/src/test/R/chiSquareTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate ChiSquare tests in
+# org.apache.commons.math.stat.inference.ChiSquareTestTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+#chisq.test(x, y = NULL, correct = TRUE,
+# p = rep(1/length(x), length(x)),
+# simulate.p.value = FALSE, B = 2000)
+#------------------------------------------------------------------------------
+tol <- 1E-9 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+verifyTable <- function(counts, expectedP, expectedStat, tol, desc) {
+ results <- chisq.test(counts)
+ if (assertEquals(expectedP, results$p.value, tol, "p-value")) {
+ displayPadded(c(desc," p-value test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " p-value test"), FAILED, WIDTH)
+ }
+ if (assertEquals(expectedStat, results$statistic, tol,
+ "ChiSquare Statistic")) {
+ displayPadded(c(desc, " chi-square statistic test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " chi-square statistic test"), FAILED, WIDTH)
+ }
+}
+
+verifyHomogeneity <- function(obs, exp, expectedP, expectedStat,
+ tol, desc) {
+ results <- chisq.test(obs,p=exp,rescale.p=TRUE)
+ chi <- results$statistic
+ p <- results$p.value
+ if (assertEquals(expectedP, p, tol, "p-value")) {
+ displayPadded(c(desc, " p-value test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " p-value test"), FAILED, WIDTH)
+ }
+ if (assertEquals(expectedStat, chi, tol,
+ "ChiSquare Statistic")) {
+ displayPadded(c(desc, " chi-square statistic test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " chi-square statistic test"), FAILED, WIDTH)
+ }
+}
+
+cat("ChiSquareTest test cases\n")
+
+observed <- c(10, 9, 11)
+expected <- c(10, 10, 10)
+verifyHomogeneity(observed, expected, 0.904837418036, 0.2, tol,
+ "testChiSquare1")
+
+observed <- c(500, 623, 72, 70, 31)
+expected <- c(485, 541, 82, 61, 37)
+verifyHomogeneity(observed, expected, 0.06051952647453607, 9.023307936427388,
+ tol, "testChiSquare2")
+
+observed <- c(2372383, 584222, 257170, 17750155, 7903832, 489265,
+ 209628, 393899)
+expected <- c(3389119.5, 649136.6, 285745.4, 25357364.76, 11291189.78,
+ 543628.0, 232921.0, 437665.75)
+verifyHomogeneity(observed, expected, 0, 114875.90421929007, tol,
+ "testChiSquareLargeTestStatistic")
+
+counts <- matrix(c(40, 22, 43, 91, 21, 28, 60, 10, 22), nc = 3);
+verifyTable(counts, 0.000144751460134, 22.709027688, tol,
+ "testChiSquareIndependence1")
+
+counts <- matrix(c(10, 15, 30, 40, 60, 90), nc = 3);
+verifyTable(counts, 0.918987499852, 0.168965517241, tol,
+ "testChiSquareIndependence2")
+
+counts <- matrix(c(40, 0, 4, 91, 1, 2, 60, 2, 0), nc = 3);
+verifyTable(counts, 0.0462835770603, 9.67444662263, tol,
+ "testChiSquareZeroCount")
+
+displayDashes(WIDTH)
+
+
diff --git a/src/test/R/correlationTestCases b/src/test/R/correlationTestCases
new file mode 100644
index 0000000..2e3b767
--- /dev/null
+++ b/src/test/R/correlationTestCases
@@ -0,0 +1,225 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Pearson's correlation tests in
+# org.apache.commons.math.stat.correlation.PearsonsCorrelationTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#------------------------------------------------------------------------------
+tol <- 1E-15 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+options(digits=16) # override number of digits displayed
+
+# Verify Pearson's correlation
+verifyPearsonsCorrelation <- function(matrix, expectedCorrelation, name) {
+ correlation <- cor(matrix)
+ output <- c("Pearson's Correlation matrix test dataset = ", name)
+ if (assertEquals(expectedCorrelation, correlation,tol,"Pearson's Correlations")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# Verify Spearman's correlation
+verifySpearmansCorrelation <- function(matrix, expectedCorrelation, name) {
+ correlation <- cor(matrix, method="spearman")
+ output <- c("Spearman's Correlation matrix test dataset = ", name)
+ if (assertEquals(expectedCorrelation, correlation,tol,"Spearman's Correlations")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify p-values
+verifyPValues <- function(matrix, pValues, name) {
+ dimension <- dim(matrix)[2]
+ corValues <- matrix(nrow=dimension,ncol=dimension)
+ expectedValues <- matrix(nrow=dimension,ncol=dimension)
+ for (i in 2:dimension) {
+ for (j in 1:(i-1)) {
+ corValues[i,j]<-cor.test(matrix[,i], matrix[,j])$p.value
+ corValues[j,i]<-corValues[i,j]
+ }
+ }
+ for (i in 1:dimension) {
+ corValues[i,i] <- 1
+ expectedValues[i,i] <- 1
+ }
+ ptr <- 1
+ for (i in 2:dimension) {
+ for (j in 1:(i-1)) {
+ expectedValues[i,j] <- pValues[ptr]
+ expectedValues[j,i] <- expectedValues[i,j]
+ ptr <- ptr + 1
+ }
+ }
+ output <- c("Correlation p-values test dataset = ", name)
+ if (assertEquals(expectedValues, corValues,tol,"p-values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ }
+
+#--------------------------------------------------------------------------
+cat("Correlation test cases\n")
+
+# Longley -----------------------------------------------------------------
+longley <- matrix(c(60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962),
+ nrow = 16, ncol = 7, byrow = TRUE)
+
+# Pearson's
+expectedCorrelation <- matrix(c(
+ 1.000000000000000, 0.9708985250610560, 0.9835516111796693, 0.5024980838759942,
+ 0.4573073999764817, 0.960390571594376, 0.9713294591921188,
+ 0.970898525061056, 1.0000000000000000, 0.9915891780247822, 0.6206333925590966,
+ 0.4647441876006747, 0.979163432977498, 0.9911491900672053,
+ 0.983551611179669, 0.9915891780247822, 1.0000000000000000, 0.6042609398895580,
+ 0.4464367918926265, 0.991090069458478, 0.9952734837647849,
+ 0.502498083875994, 0.6206333925590966, 0.6042609398895580, 1.0000000000000000,
+ -0.1774206295018783, 0.686551516365312, 0.6682566045621746,
+ 0.457307399976482, 0.4647441876006747, 0.4464367918926265, -0.1774206295018783,
+ 1.0000000000000000, 0.364416267189032, 0.4172451498349454,
+ 0.960390571594376, 0.9791634329774981, 0.9910900694584777, 0.6865515163653120,
+ 0.3644162671890320, 1.000000000000000, 0.9939528462329257,
+ 0.971329459192119, 0.9911491900672053, 0.9952734837647849, 0.6682566045621746,
+ 0.4172451498349454, 0.993952846232926, 1.0000000000000000),
+ nrow = 7, ncol = 7, byrow = TRUE)
+ verifyPearsonsCorrelation(longley, expectedCorrelation, "longley")
+
+ expectedPValues <- c(
+ 4.38904690369668e-10,
+ 8.36353208910623e-12, 7.8159700933611e-14,
+ 0.0472894097790304, 0.01030636128354301, 0.01316878049026582,
+ 0.0749178049642416, 0.06971758330341182, 0.0830166169296545, 0.510948586323452,
+ 3.693245043123738e-09, 4.327782576751815e-11, 1.167954621905665e-13, 0.00331028281967516, 0.1652293725106684,
+ 3.95834476307755e-10, 1.114663916723657e-13, 1.332267629550188e-15, 0.00466039138541463, 0.1078477071581498, 7.771561172376096e-15)
+ verifyPValues(longley, expectedPValues, "longley")
+
+ # Spearman's
+expectedCorrelation <- matrix(c(
+ 1, 0.982352941176471, 0.985294117647059, 0.564705882352941, 0.2264705882352941, 0.976470588235294,
+ 0.976470588235294, 0.982352941176471, 1, 0.997058823529412, 0.664705882352941, 0.2205882352941176,
+ 0.997058823529412, 0.997058823529412, 0.985294117647059, 0.997058823529412, 1, 0.638235294117647,
+ 0.2235294117647059, 0.9941176470588236, 0.9941176470588236, 0.564705882352941, 0.664705882352941,
+ 0.638235294117647, 1, -0.3411764705882353, 0.685294117647059, 0.685294117647059, 0.2264705882352941,
+ 0.2205882352941176, 0.2235294117647059, -0.3411764705882353, 1, 0.2264705882352941, 0.2264705882352941,
+ 0.976470588235294, 0.997058823529412, 0.9941176470588236, 0.685294117647059, 0.2264705882352941, 1, 1,
+ 0.976470588235294, 0.997058823529412, 0.9941176470588236, 0.685294117647059, 0.2264705882352941, 1, 1),
+ nrow = 7, ncol = 7, byrow = TRUE)
+ verifySpearmansCorrelation(longley, expectedCorrelation, "longley")
+
+ # Swiss Fertility ---------------------------------------------------------
+ fertility <- matrix(c(80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33),
+ nrow = 47, ncol = 5, byrow = TRUE)
+
+# Pearson's
+ expectedCorrelation <- matrix(c(
+ 1, 0.3530791836199747, -0.6458827064572875, -0.663788857035069, 0.463684700651794,
+ 0.3530791836199747, 1, -0.6865422086171366, -0.63952251894832, 0.4010950530487398,
+ -0.6458827064572875, -0.6865422086171366, 1, 0.698415296288483, -0.572741806064167,
+ -0.663788857035069, -0.63952251894832, 0.698415296288483, 1, -0.1538589170909148,
+ 0.463684700651794, 0.4010950530487398, -0.572741806064167, -0.1538589170909148, 1),
+ nrow = 5, ncol = 5, byrow = TRUE)
+verifyPearsonsCorrelation(fertility, expectedCorrelation, "swiss fertility")
+
+expectedPValues <- c(
+ 0.01491720061472623,
+ 9.45043734069043e-07, 9.95151527133974e-08,
+ 3.658616965962355e-07, 1.304590105694471e-06, 4.811397236181847e-08,
+ 0.001028523190118147, 0.005204433539191644, 2.588307925380906e-05, 0.301807756132683)
+verifyPValues(fertility, expectedPValues, "swiss fertility")
+
+# Spearman's
+expectedCorrelation <- matrix(c(
+ 1, 0.2426642769364176, -0.660902996352354, -0.443257690360988, 0.4136455623012432,
+ 0.2426642769364176, 1, -0.598859938748963, -0.650463814145816, 0.2886878090882852,
+ -0.660902996352354, -0.598859938748963, 1, 0.674603831406147, -0.4750575257171745,
+ -0.443257690360988, -0.650463814145816, 0.674603831406147, 1, -0.1444163088302244,
+ 0.4136455623012432, 0.2886878090882852, -0.4750575257171745, -0.1444163088302244, 1),
+ nrow = 5, ncol = 5, byrow = TRUE)
+ verifySpearmansCorrelation(fertility, expectedCorrelation, "swiss fertility")
+
+displayDashes(WIDTH)
diff --git a/src/test/R/covarianceTestCases b/src/test/R/covarianceTestCases
new file mode 100644
index 0000000..3d8d603
--- /dev/null
+++ b/src/test/R/covarianceTestCases
@@ -0,0 +1,146 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#------------------------------------------------------------------------------
+# R source file to validate covariance tests in
+# org.apache.commons.math.stat.correlation.CovarianceTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#------------------------------------------------------------------------------
+tol <- 1E-9 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+options(digits=16) # override number of digits displayed
+
+# function to verify covariance computations
+verifyCovariance <- function(matrix, expectedCovariance, name) {
+ covariance <- cov(matrix)
+ output <- c("Covariance test dataset = ", name)
+ if (assertEquals(expectedCovariance,covariance,tol,"Covariances")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Covariance test cases\n")
+
+# Longley
+
+longley <- matrix(c(60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962),
+ nrow = 16, ncol = 7, byrow = TRUE)
+
+expectedCovariance <- matrix(c(
+ 12333921.73333333246, 3.679666000000000e+04, 343330206.333333313,
+ 1649102.666666666744, 1117681.066666666651, 23461965.733333334, 16240.93333333333248,
+ 36796.66000000000, 1.164576250000000e+02, 1063604.115416667,
+ 6258.666250000000, 3490.253750000000, 73503.000000000, 50.92333333333334,
+ 343330206.33333331347, 1.063604115416667e+06, 9879353659.329166412,
+ 56124369.854166664183, 30880428.345833335072, 685240944.600000024, 470977.90000000002328,
+ 1649102.66666666674, 6.258666250000000e+03, 56124369.854166664,
+ 873223.429166666698, -115378.762499999997, 4462741.533333333, 2973.03333333333330,
+ 1117681.06666666665, 3.490253750000000e+03, 30880428.345833335,
+ -115378.762499999997, 484304.095833333326, 1764098.133333333, 1382.43333333333339,
+ 23461965.73333333433, 7.350300000000000e+04, 685240944.600000024,
+ 4462741.533333333209, 1764098.133333333302, 48387348.933333330, 32917.40000000000146,
+ 16240.93333333333, 5.092333333333334e+01, 470977.900000000,
+ 2973.033333333333, 1382.433333333333, 32917.40000000, 22.66666666666667),
+ nrow = 7, ncol = 7, byrow = TRUE)
+
+ verifyCovariance(longley, expectedCovariance, "longley")
+
+ # Swiss Fertility
+
+ fertility <- matrix(c(80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33),
+ nrow = 47, ncol = 5, byrow = TRUE)
+
+ expectedCovariance <- matrix(c(
+ 156.0424976873265, 100.1691489361702, -64.36692876965772, -79.7295097132285, 241.5632030527289,
+ 100.169148936170251, 515.7994172062905, -124.39283071230344, -139.6574005550416, 379.9043755781684,
+ -64.3669287696577, -124.3928307123034, 63.64662349676226, 53.5758556891767, -190.5606105457909,
+ -79.7295097132285, -139.6574005550416, 53.57585568917669, 92.4560592044403, -61.6988297872340,
+ 241.5632030527289, 379.9043755781684, -190.56061054579092, -61.6988297872340, 1739.2945371877890),
+ nrow = 5, ncol = 5, byrow = TRUE)
+
+ verifyCovariance(fertility, expectedCovariance, "swiss fertility")
+
+ displayDashes(WIDTH)
\ No newline at end of file
diff --git a/src/test/R/descriptiveTestCases b/src/test/R/descriptiveTestCases
new file mode 100644
index 0000000..c724567
--- /dev/null
+++ b/src/test/R/descriptiveTestCases
@@ -0,0 +1,83 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#------------------------------------------------------------------------------
+# R source file to validate summary statistics tests in
+# org.apache.commons.math.stat.CertifiedDataTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# mean(x)
+# sd(x)
+#------------------------------------------------------------------------------
+tol <- 1E-14 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+options(digits=15) # bump displayed digits to 15
+
+verifyMean <- function(values, expectedMean, tol, desc) {
+ results <- mean(values)
+ if (assertEquals(expectedMean, results, tol, "mean")) {
+ displayPadded(c(desc," mean test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " mean test"), FAILED, WIDTH)
+ }
+}
+
+verifySigma <- function(values, expectedSigma, tol, desc) {
+ results <- sd(values)
+ if (assertEquals(expectedSigma, results, tol, "std")) {
+ displayPadded(c(desc," std test"), SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(c(desc, " std test"), FAILED, WIDTH)
+ }
+}
+
+cat("Descriptive test cases\n")
+
+# Michelson data
+values <- c(299.85,299.74,299.90,300.07,299.93,299.85,299.95,299.98,299.98,
+299.88,300.00,299.98,299.93,299.65,299.76,299.81,300.00,300.00,299.96,299.96,
+299.96,299.94,299.96,299.94,299.88,299.80,299.85,299.88,299.90,299.84,299.83,
+299.79,299.81,299.88,299.88,299.83,299.80,299.79,299.76,299.80,299.88,299.88,
+299.88,299.86,299.72,299.72,299.62,299.86,299.97,299.95,299.88,299.91,299.85,
+299.87,299.84,299.84,299.85,299.84,299.84,299.84,299.89,299.81,299.81,299.82,
+299.80,299.77,299.76,299.74,299.75,299.76,299.91,299.92,299.89,299.86,299.88,
+299.72,299.84,299.85,299.85,299.78,299.89,299.84,299.78,299.81,299.76,299.81,
+299.79,299.81,299.82,299.85,299.87,299.87,299.81,299.74,299.81,299.94,299.95,
+299.80,299.81,299.87)
+expectedMean <- 299.852400000000
+expectedSigma <- 0.0790105478190518
+verifyMean(values, expectedMean, tol, "Michelson")
+verifySigma(values, expectedSigma, tol, "Michelson")
+
+# Mavro data
+values <- c(2.00180,2.00170,2.00180,2.00190,2.00180,2.00170,2.00150,2.00140,
+2.00150,2.00150,2.00170,2.00180,2.00180,2.00190,2.00190,2.00210,2.00200,
+2.00160,2.00140,2.00130,2.00130,2.00150,2.00150,2.00160,2.00150,2.00140,
+2.00130,2.00140,2.00150,2.00140,2.00150,2.00160,2.00150,2.00160,2.00190,
+2.00200,2.00200,2.00210,2.00220,2.00230,2.00240,2.00250,2.00270,2.00260,
+2.00260,2.00260,2.00270,2.00260,2.00250,2.00240)
+expectedMean <- 2.00185600000000
+expectedSigma <- 0.000429123454003053
+verifyMean(values, expectedMean, tol, "Mavro")
+verifySigma(values, expectedSigma, tol, "Mavro")
+
+displayDashes(WIDTH)
diff --git a/src/test/R/exponentialTestCases b/src/test/R/exponentialTestCases
new file mode 100644
index 0000000..25f801a
--- /dev/null
+++ b/src/test/R/exponentialTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate exponential distribution tests in
+# org.apache.commons.math.distribution.ExponentialDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#------------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, mean, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pexp(point, 1/mean)
+ }
+ output <- c("Distribution test mean = ", mean)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+verifyDensity <- function(points, expected, mean, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dexp(point, 1/mean)
+ }
+ output <- c("Density test mean = ", mean)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify quantiles
+verifyQuantiles <- function(points, expected, mean, tol) {
+ rQuantileValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rQuantileValues[i] <- qexp(point, 1/mean, log = FALSE)
+ }
+ output <- c("Quantile test mean = ", mean)
+ if (assertEquals(expected, rQuantileValues, tol, "Quantile Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+
+#--------------------------------------------------------------------------
+cat("Exponential test cases\n")
+
+mean <- 5
+
+distributionValues <- c(0, 0, 0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.2, 0.2, 0.1998, 0.198, 0.195, 0.19, 0.18, 0.000200000000000,
+ 0.00200000000002, 0.00499999999997, 0.00999999999994, 0.0199999999999)
+distributionPoints <- c(0, 0, 0.00500250166792, 0.0502516792675, 0.126589039921, 0.256466471938,
+ 0.526802578289, 34.5387763949, 23.0258509299, 18.4443972706, 14.9786613678, 11.5129254650)
+verifyDistribution(distributionPoints, distributionValues, mean, tol)
+verifyQuantiles(distributionValues, distributionPoints, mean, tol)
+verifyDensity(distributionPoints, densityValues, mean, tol)
+
+output <- "Probability test P(.25 < X < .75)"
+if (assertEquals(0.0905214480757, pexp(.75, 1/mean) - pexp(.25, 1/mean), tol, "Probability value")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+displayDashes(WIDTH)
diff --git a/src/test/R/hypergeometricTestCases b/src/test/R/hypergeometricTestCases
new file mode 100644
index 0000000..a912fa5
--- /dev/null
+++ b/src/test/R/hypergeometricTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Hypergeometric distribution tests in
+# org.apache.commons.math.distribution.HypergeometricDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# dhyper(x, m, n, k, log = FALSE) <- density
+# phyper(q, m, n, k, lower.tail = TRUE, log.p = FALSE) <- distribution
+# qhyper(p, m, n, k, lower.tail = TRUE, log.p = FALSE) <- quantiles
+#------------------------------------------------------------------------------
+tol <- 1E-6 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, good, bad, selected, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dhyper(point, good, bad, selected)
+ }
+ output <- c("Density test good = ", good, ", bad = ", bad,
+ ", selected = ",selected)
+ if (assertEquals(expected,rDensityValues,tol,"Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, good, bad, selected, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- phyper(point, good, bad, selected)
+ }
+ output <- c("Distribution test good = ", good, ", bad = ",
+ bad, ", selected = ",selected)
+ if (assertEquals(expected,rDistValues,tol,"Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Hypergeometric test cases\n")
+
+good <- 5
+bad <- 5
+selected <- 5
+
+densityPoints <- c(-1, 0, 1, 2, 3, 4, 5, 10)
+densityValues <- c(0, 0.003968, 0.099206, 0.396825, 0.396825, 0.099206,
+ 0.003968, 0)
+distributionValues <- c(0, .003968, .103175, .50000, .896825, .996032,
+ 1.00000, 1)
+#Eliminate p=1 case because it will mess up adjustement below
+inverseCumPoints <- c(0, 0.001, 0.010, 0.025, 0.050, 0.100, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+inverseCumValues <- c(-1, -1, 0, 0, 0, 0, 4, 3, 3, 3, 3)
+
+verifyDensity(densityPoints, densityValues, good, bad, selected, tol)
+verifyDistribution(densityPoints, distributionValues, good, bad, selected, tol)
+
+i <- 0
+rInverseCumValues <- rep(0,length(inverseCumPoints))
+for (point in inverseCumPoints) {
+ i <- i + 1
+ rInverseCumValues[i] <- qhyper(point, good, bad, selected)
+}
+
+output <- c("Inverse Distribution test good = ", good, ", bad = ", bad,
+ ", selected = ", selected)
+# R defines quantiles from the right, need to subtract one
+if (assertEquals(inverseCumValues, rInverseCumValues-1, tol,
+ "Inverse Dist Values")) {
+ displayPadded(output, SUCCEEDED, 80)
+} else {
+ displayPadded(output, FAILED, 80)
+}
+
+# Degenerate cases
+good <- 5
+bad <- 0
+selected <- 3
+densityPoints <- c(-1, 0, 1, 3, 10)
+densityValues <- c(0, 0, 0, 1, 0)
+distributionValues <- c(0, 0, 0, 1, 1)
+verifyDensity(densityPoints, densityValues, good, bad, selected, tol)
+verifyDistribution(densityPoints, distributionValues, good, bad, selected, tol)
+
+good <- 0
+bad <- 5
+selected <- 3
+densityPoints <- c(-1, 0, 1, 3, 10)
+densityValues <- c(0, 1, 0, 0, 0)
+distributionValues <- c(0, 1, 1, 1, 1)
+verifyDensity(densityPoints, densityValues, good, bad, selected, tol)
+verifyDistribution(densityPoints, distributionValues, good, bad, selected, tol)
+
+good <- 3
+bad <- 2
+selected <- 5
+densityPoints <- c(-1, 0, 1, 3, 10)
+densityValues <- c(0, 0, 0, 1, 0)
+distributionValues <- c(0, 0, 0, 1, 1)
+verifyDensity(densityPoints, densityValues, good, bad, selected, tol)
+verifyDistribution(densityPoints, distributionValues, good, bad, selected, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/multipleOLSRegressionTestCases b/src/test/R/multipleOLSRegressionTestCases
new file mode 100644
index 0000000..1fe983d
--- /dev/null
+++ b/src/test/R/multipleOLSRegressionTestCases
@@ -0,0 +1,309 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#------------------------------------------------------------------------------
+# R source file to validate OLS multiple regression tests in
+# org.apache.commons.math.stat.regression.OLSMultipleLinearRegressionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+#------------------------------------------------------------------------------
+tol <- 1E-8 # default error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+options(digits=16) # override number of digits displayed
+
+# function to verify OLS computations
+
+verifyRegression <- function(model, expectedBeta, expectedResiduals,
+ expectedErrors, expectedStdError, expectedRSquare, expecteAdjRSquare, modelName) {
+ betaHat <- as.vector(coefficients(model))
+ residuals <- as.vector(residuals(model))
+ errors <- as.vector(as.matrix(coefficients(summary(model)))[,2])
+ stdError <- summary(model)$sigma
+ rSquare <- summary(model)$r.squared
+ adjRSquare <- summary(model)$adj.r.squared
+ output <- c("Parameter test dataset = ", modelName)
+ if (assertEquals(expectedBeta,betaHat,tol,"Parameters")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("Residuals test dataset = ", modelName)
+ if (assertEquals(expectedResiduals,residuals,tol,"Residuals")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("Errors test dataset = ", modelName)
+ if (assertEquals(expectedErrors,errors,tol,"Errors")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("Standard Error test dataset = ", modelName)
+ if (assertEquals(expectedStdError,stdError,tol,"Regression Standard Error")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("RSquared test dataset = ", modelName)
+ if (assertEquals(expectedRSquare,rSquare,tol,"RSquared")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+ output <- c("Adjusted RSquared test dataset = ", modelName)
+ if (assertEquals(expecteAdjRSquare,adjRSquare,tol,"Adjusted RSquared")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Multiple regression OLS test cases\n")
+
+# Perfect fit
+x1 <- c(0,2,0,0,0,0)
+x2 <- c(0,0,3,0,0,0)
+x3 <- c(0,0,0,4,0,0)
+x4 <- c(0,0,0,0,5,0)
+x5 <- c(0,0,0,0,0,6)
+y <- c(11, 12, 13, 14, 15, 16)
+model <- lm(y ~ x1 + x2 + x3 + x4 + x5)
+expectedBeta <- c(11.0,0.5,0.666666666666667,0.75,0.8,0.8333333333333333)
+expectedResiduals <- c(0,0,0,0,0,0)
+expectedErrors <- c(NaN,NaN,NaN,NaN,NaN,NaN)
+expectedStdError <- NaN
+expectedRSquare <- 1
+expectedAdjRSquare <- NaN
+verifyRegression(model, expectedBeta, expectedResiduals, expectedErrors,
+ expectedStdError, expectedRSquare, expectedAdjRSquare, "perfect fit")
+
+# Longley
+#
+# Data Source: J. Longley (1967) "An Appraisal of Least Squares Programs for the
+# Electronic Computer from the Point of View of the User",
+# Journal of the American Statistical Association,
+# vol. 62. September, pp. 819-841.
+#
+# Certified values (and data) are from NIST:
+# http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Longley.dat
+#
+design <- matrix(c(60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962),
+ nrow = 16, ncol = 7, byrow = TRUE)
+y <- design[,1]
+x1 <- design[,2]
+x2 <- design[,3]
+x3 <- design[,4]
+x4 <- design[,5]
+x5 <- design[,6]
+x6 <- design[,7]
+model <- lm(y ~ x1 + x2 + x3 + x4 + x5 + x6)
+
+estimates <- matrix(c(-3482258.63459582,890420.383607373,
+ 15.0618722713733,84.9149257747669,
+ -0.358191792925910E-01,0.334910077722432E-01,
+ -2.02022980381683,0.488399681651699,
+ -1.03322686717359,0.214274163161675,
+ -0.511041056535807E-01,0.226073200069370,
+ 1829.15146461355,455.478499142212),
+ nrow = 7, ncol = 2, byrow = TRUE)
+
+expectedBeta <- estimates[,1]
+expectedErrors <- estimates[,2]
+expectedResiduals <- c(267.340029759711,-94.0139423988359,46.28716775752924,
+ -410.114621930906,309.7145907602313,-249.3112153297231,-164.0489563956039,
+ -13.18035686637081,14.30477260005235,455.394094551857,-17.26892711483297,
+ -39.0550425226967,-155.5499735953195,-85.6713080421283,341.9315139607727,
+ -206.7578251937366)
+expectedStdError <- 304.8540735619638
+expectedRSquare <- 0.995479004577296
+expectedAdjRSquare <- 0.992465007628826
+
+verifyRegression(model, expectedBeta, expectedResiduals, expectedErrors,
+expectedStdError, expectedRSquare, expectedAdjRSquare, "Longley")
+
+# Model with no intercept
+model <- lm(y ~ 0 + x1 + x2 + x3 + x4 + x5 + x6)
+
+estimates <- matrix(c(-52.99357013868291, 129.54486693117232,
+ 0.07107319907358, 0.03016640003786,
+ -0.42346585566399, 0.41773654056612,
+ -0.57256866841929, 0.27899087467676,
+ -0.41420358884978, 0.32128496193363,
+ 48.41786562001326, 17.68948737819961),
+ nrow = 6, ncol = 2, byrow = TRUE)
+
+expectedBeta <- estimates[,1]
+expectedErrors <- estimates[,2]
+expectedResiduals <- c(279.90274927293092, -130.32465380836874, 90.73228661967445,
+ -401.31252201634948, -440.46768772620027, -543.54512853774793, 201.32111639536299,
+ 215.90889365977932, 73.09368242049943, 913.21694494481869, 424.82484953610174,
+ -8.56475876776709, -361.32974610842876, 27.34560497213464, 151.28955976355002,
+ -492.49937355336846)
+expectedStdError <- 475.1655079819517
+expectedRSquare <- 0.9999670130706
+expectedAdjRSquare <- 0.999947220913
+
+verifyRegression(model, expectedBeta, expectedResiduals, expectedErrors,
+expectedStdError, expectedRSquare, expectedAdjRSquare, "Longley No Intercept")
+
+# Swiss Fertility (R dataset named "swiss")
+
+design <- matrix(c(80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33),
+ nrow = 47, ncol = 5, byrow = TRUE)
+
+y <- design[,1]
+x1 <- design[,2]
+x2 <- design[,3]
+x3 <- design[,4]
+x4 <- design[,5]
+
+model <- lm(y ~ x1 + x2 + x3 + x4)
+
+estimates <- matrix(c(91.05542390271397,6.94881329475087,
+ -0.22064551045715,0.07360008972340,
+ -0.26058239824328,0.27410957467466,
+ -0.96161238456030,0.19454551679325,
+ 0.12441843147162,0.03726654773803),
+ nrow = 5, ncol = 2, byrow = TRUE)
+
+expectedBeta <- estimates[,1]
+expectedErrors <- estimates[,2]
+
+expectedResiduals <- c(7.1044267859730512,1.6580347433531366,
+ 4.6944952770029644,8.4548022690166160,13.6547432343186212,
+ -9.3586864458500774,7.5822446330520386,15.5568995563859289,
+ 0.8113090736598980,7.1186762732484308,7.4251378771228724,
+ 2.6761316873234109,0.8351584810309354,7.1769991119615177,
+ -3.8746753206299553,-3.1337779476387251,-0.1412575244091504,
+ 1.1186809170469780,-6.3588097346816594,3.4039270429434074,
+ 2.3374058329820175,-7.9272368576900503,-7.8361010968497959,
+ -11.2597369269357070,0.9445333697827101,6.6544245101380328,
+ -0.9146136301118665,-4.3152449403848570,-4.3536932047009183,
+ -3.8907885169304661,-6.3027643926302188,-7.8308982189289091,
+ -3.1792280015332750,-6.7167298771158226,-4.8469946718041754,
+ -10.6335664353633685,11.1031134362036958,6.0084032641811733,
+ 5.4326230830188482,-7.2375578629692230,2.1671550814448222,
+ 15.0147574652763112,4.8625103516321015,-7.1597256413907706,
+ -0.4515205619767598,-10.2916870903837587,-15.7812984571900063)
+
+expectedStdError <- 7.73642194433223
+expectedRSquare <- 0.649789742860228
+expectedAdjRSquare <- 0.6164363850373927
+
+verifyRegression(model, expectedBeta, expectedResiduals, expectedErrors,
+expectedStdError, expectedRSquare, expectedAdjRSquare, "Swiss Fertility")
+
+# model with no intercept
+model <- lm(y ~ 0 + x1 + x2 + x3 + x4)
+
+estimates <- matrix(c(0.52191832900513, 0.10470063765677,
+ 2.36588087917963, 0.41684100584290,
+ -0.94770353802795, 0.43370143099691,
+ 0.30851985863609, 0.07694953606522),
+ nrow = 4, ncol = 2, byrow = TRUE)
+
+expectedBeta <- estimates[,1]
+expectedErrors <- estimates[,2]
+
+expectedResiduals <- c(44.138759883538249, 27.720705122356215, 35.873200836126799,
+ 34.574619581211977, 26.600168342080213, 15.074636243026923, -12.704904871199814,
+ 1.497443824078134, 2.691972687079431, 5.582798774291231, -4.422986561283165,
+ -9.198581600334345, 4.481765170730647, 2.273520207553216, -22.649827853221336,
+ -17.747900013943308, 20.298314638496436, 6.861405135329779, -8.684712790954924,
+ -10.298639278062371, -9.896618896845819, 4.568568616351242, -15.313570491727944,
+ -13.762961360873966, 7.156100301980509, 16.722282219843990, 26.716200609071898,
+ -1.991466398777079, -2.523342564719335, 9.776486693095093, -5.297535127628603,
+ -16.639070567471094, -10.302057295211819, -23.549487860816846, 1.506624392156384,
+ -17.939174438345930, 13.105792202765040, -1.943329906928462, -1.516005841666695,
+ -0.759066561832886, 20.793137744128977, -2.485236153005426, 27.588238710486976,
+ 2.658333257106881, -15.998337823623046, -5.550742066720694, -14.219077806826615)
+
+expectedStdError <- 17.24710630547
+expectedRSquare <- 0.946350722085
+expectedAdjRSquare <- 0.9413600915813
+
+verifyRegression(model, expectedBeta, expectedResiduals, expectedErrors,
+expectedStdError, expectedRSquare, expectedAdjRSquare, "Swiss Fertility No Intercept")
+
+displayDashes(WIDTH)
diff --git a/src/test/R/normalTestCases b/src/test/R/normalTestCases
new file mode 100644
index 0000000..ae54a96
--- /dev/null
+++ b/src/test/R/normalTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Normal distribution tests in
+# org.apache.commons.math.distribution.NormalDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# pnorm(q, mean=0, sd=1, lower.tail = TRUE, log.p = FALSE) <-- distribution
+#-----------------------------------------------------------------------------
+tol <- 1E-9
+
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, mu, sigma, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pnorm(point, mu, sigma, log = FALSE)
+ }
+ output <- c("Distribution test mu = ",mu,", sigma = ", sigma)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, mu, sigma, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dnorm(point, mu, sigma, log = FALSE)
+ }
+ output <- c("Density test mu = ",mu,", sigma = ", sigma)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Normal test cases\n")
+
+mu <- 2.1
+sigma <- 1.4
+distributionValues <- c(0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+densityValues <- c(0.00240506434076, 0.0190372444310, 0.0417464784322, 0.0736683145538, 0.125355951380,
+ 0.00240506434076, 0.0190372444310, 0.0417464784322, 0.0736683145538, 0.125355951380)
+distributionPoints <- c(-2.226325228634938, -1.156887023657177, -0.643949578356075, -0.2027950777320613, 0.305827808237559,
+ 6.42632522863494, 5.35688702365718, 4.843949578356074, 4.40279507773206, 3.89417219176244)
+verifyDistribution(distributionPoints, distributionValues, mu, sigma, tol)
+verifyDensity(distributionPoints, densityValues, mu, sigma, tol)
+
+distributionValues <- c( 0.0227501319482, 0.158655253931, 0.5, 0.841344746069, 0.977249868052,
+ 0.998650101968, 0.999968328758, 0.999999713348)
+densityValues <- c(0.0385649760808, 0.172836231799, 0.284958771715, 0.172836231799, 0.0385649760808,
+ 0.00316560600853, 9.55930184035e-05, 1.06194251052e-06)
+distributionPoints <- c(mu - 2 *sigma, mu - sigma, mu, mu + sigma,
+ mu + 2 * sigma, mu + 3 * sigma, mu + 4 * sigma,
+ mu + 5 * sigma)
+verifyDistribution(distributionPoints, distributionValues, mu, sigma, tol)
+verifyDensity(distributionPoints, densityValues, mu, sigma, tol)
+
+mu <- 0
+sigma <- 1
+distributionPoints <- c(mu - 2 *sigma, mu - sigma, mu, mu + sigma,
+ mu + 2 * sigma, mu + 3 * sigma, mu + 4 * sigma,
+ mu + 5 * sigma)
+densityValues <- c(0.0539909665132, 0.241970724519, 0.398942280401, 0.241970724519, 0.0539909665132,
+ 0.00443184841194, 0.000133830225765, 1.48671951473e-06)
+verifyDistribution(distributionPoints, distributionValues, mu, sigma, tol)
+verifyDensity(distributionPoints, densityValues, mu, sigma, tol)
+
+mu <- 0
+sigma <- 0.1
+distributionPoints <- c(mu - 2 *sigma, mu - sigma, mu, mu + sigma,
+ mu + 2 * sigma, mu + 3 * sigma, mu + 4 * sigma,
+ mu + 5 * sigma)
+densityValues <- c(0.539909665132, 2.41970724519, 3.98942280401, 2.41970724519,
+ 0.539909665132, 0.0443184841194, 0.00133830225765, 1.48671951473e-05)
+verifyDistribution(distributionPoints, distributionValues, mu, sigma, tol)
+verifyDensity(distributionPoints, densityValues, mu, sigma, tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/pascalTestCases b/src/test/R/pascalTestCases
new file mode 100644
index 0000000..a8c6c31
--- /dev/null
+++ b/src/test/R/pascalTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Pascal distribution tests in
+# org.apache.commons.math.distribution.PascalDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# dnbinom(x, size, prob, mu, log = FALSE) <- density
+# pnbinom(q, size, prob, mu, lower.tail = TRUE, log.p = FALSE) <- distribution
+# qnbinom(p, size, prob, mu, lower.tail = TRUE, log.p = FALSE) <- quantiles
+#------------------------------------------------------------------------------
+tol <- 1E-9 # error tolerance for tests
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, size, p, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dnbinom(point, size, p)
+ }
+ output <- c("Density test size = ", size, ", p = ", p)
+ if (assertEquals(expected,rDensityValues,tol,"Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, size, p, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- pnbinom(point, size, p)
+ }
+ output <- c("Distribution test size = ", size, ", p = ", p)
+ if (assertEquals(expected,rDistValues,tol,"Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+#--------------------------------------------------------------------------
+cat("Negative Binomial test cases\n")
+
+size <- 10.0
+probability <- 0.70
+
+densityPoints <- c(-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
+densityValues <- c(0, 0.0282475249, 0.0847425747, 0.139825248255, 0.167790297906, 0.163595540458,
+ 0.137420253985, 0.103065190489, 0.070673273478, 0.0450542118422, 0.0270325271053,
+ 0.0154085404500, 0.0084046584273)
+distributionValues <- c(0, 0.0282475249, 0.1129900996, 0.252815347855, 0.420605645761, 0.584201186219,
+ 0.721621440204, 0.824686630693, 0.895359904171, 0.940414116013, 0.967446643119,
+ 0.982855183569, 0.991259841996)
+inverseCumPoints <- c( 0, 0.001, 0.010, 0.025, 0.050, 0.100, 0.999,
+ 0.990, 0.975, 0.950, 0.900)
+inverseCumValues <- c(-1, -1, -1, -1, 0, 0, 13, 10, 9, 8, 7)
+
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+verifyDistribution(densityPoints, distributionValues, size, probability, tol)
+
+i <- 0
+rInverseCumValues <- rep(0,length(inverseCumPoints))
+for (point in inverseCumPoints) {
+ i <- i + 1
+ rInverseCumValues[i] <- qnbinom(point, size, probability)
+}
+
+output <- c("Inverse Distribution test n = ", size, ", p = ", probability)
+# R defines quantiles from the right, need to subtract one
+if (assertEquals(inverseCumValues, rInverseCumValues-1, tol,
+ "Inverse Dist Values")) {
+ displayPadded(output, SUCCEEDED, 80)
+} else {
+ displayPadded(output, FAILED, 80)
+}
+
+# Degenerate cases
+
+size <- 5
+probability <- 0.0
+
+densityPoints <- c(-1, 0, 1, 10, 11)
+# Note: commons math returns 0's below
+densityValues <- c(NaN, NaN, NaN, NaN, NaN)
+distributionPoints <- c(-1, 0, 1, 5, 10)
+# Note: commons math returns 0's below
+distributionValues <- c(NaN, NaN, NaN, NaN, NaN)
+
+output <- c("Density test n = ", size, ", p = ", probability)
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+output <- c("Distribution test n = ", size, ", p = ", probability)
+verifyDistribution(distributionPoints,distributionValues,size,probability,tol)
+
+size <- 5
+probability <- 1.0
+
+densityPoints <- c(-1, 0, 1, 2, 5, 10)
+densityValues <- c(0, 1, 0, 0, 0, 0)
+distributionPoints <- c(-1, 0, 1, 2, 5, 10)
+distributionValues <- c(0, 1, 1, 1, 1, 1)
+
+output <- c("Density test n = ", size, ", p = ", probability)
+verifyDensity(densityPoints,densityValues,size,probability,tol)
+output <- c("Distribution test n = ", size, ", p = ", probability)
+verifyDistribution(distributionPoints,distributionValues,size,probability,tol)
+
+displayDashes(WIDTH)
diff --git a/src/test/R/poissonTestCases b/src/test/R/poissonTestCases
new file mode 100644
index 0000000..200aa75
--- /dev/null
+++ b/src/test/R/poissonTestCases
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to validate Poisson distribution tests in
+# org.apache.commons.math.distribution.PoissonDistributionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# R functions used
+# dpois(x, lambda, log = FALSE) <-- density
+# ppois(q, lambda, lower.tail = TRUE, log.p = FALSE) <-- distribution
+# pnorm(q, mean=0, sd=1, lower.tail = TRUE, log.p = FALSE) <-- normal dist.
+#------------------------------------------------------------------------------
+tol <- 1E-10
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+
+# function to verify density computations
+
+verifyDensity <- function(points, expected, lambda, tol) {
+ rDensityValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDensityValues[i] <- dpois(point, lambda, log = FALSE)
+ }
+ output <- c("Density test lambda = ", lambda)
+ if (assertEquals(expected, rDensityValues, tol, "Density Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify distribution computations
+
+verifyDistribution <- function(points, expected, lambda, tol) {
+ rDistValues <- rep(0, length(points))
+ i <- 0
+ for (point in points) {
+ i <- i + 1
+ rDistValues[i] <- ppois(point, lambda, log = FALSE)
+ }
+ output <- c("Distribution test lambda = ", lambda)
+ if (assertEquals(expected, rDistValues, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+# function to verify normal approximation
+
+verifyNormalApproximation <- function(expected, lambda, lower, upper, tol) {
+ rValue <- pnorm(upper, mean=lambda, sd=sqrt(lambda), lower.tail = TRUE,
+ log.p = FALSE) -
+ pnorm(lower, mean=lambda, sd=sqrt(lambda), lower.tail = TRUE,
+ log.p = FALSE)
+ output <- c("Normal approx. test lambda = ", lambda, " upper = ",
+ upper, " lower = ", lower)
+ if (assertEquals(expected, rValue, tol, "Distribution Values")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+ } else {
+ displayPadded(output, FAILED, WIDTH)
+ }
+}
+
+cat("Poisson distribution test cases\n")
+
+# stock tests
+
+lambda <- 4.0
+densityPoints <- c(-1,0,1,2,3,4,5,10,20)
+densityValues <- c(0, 0.0183156388887, 0.073262555555, 0.14652511111,
+ 0.195366814813, 0.195366814813, 0.156293451851,
+ 0.00529247667642, 8.27746364655e-09)
+verifyDensity(densityPoints, densityValues, lambda, tol)
+
+
+distributionPoints <- c(-1, 0, 1, 2, 3, 4, 5, 10, 20)
+distributionValues <- c(0, 0.0183156388887, 0.0915781944437, 0.238103305554,
+ 0.433470120367, 0.62883693518, 0.78513038703,
+ 0.99716023388, 0.999999998077)
+verifyDistribution(distributionPoints, distributionValues, lambda, tol)
+
+# normal approximation tests
+
+lambda <- 100
+verifyNormalApproximation(0.706281887248, lambda, 89.5, 110.5, tol)
+
+lambda <- 10000
+verifyNormalApproximation(0.820070051552, lambda, 9899.5, 10200.5, tol)
+
+displayDashes(WIDTH)
+
+
+
+
+
diff --git a/src/test/R/regressionTestCases b/src/test/R/regressionTestCases
new file mode 100644
index 0000000..c4465ca
--- /dev/null
+++ b/src/test/R/regressionTestCases
@@ -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.
+#
+#-----------------------------------------------------------------------
+# R source file to validate Binomial distribution tests in
+# org.apache.commons.math.stat.regression.SimpleRegressionTest
+#
+# To run the test, install R, put this file and testFunctions
+# into the same directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# Output will be written to a file named "regTestResults"
+# in the directory from which R was launched
+#
+#------------------------------------------------------------------------------
+tol <- 1E-8
+#------------------------------------------------------------------------------
+# Function definitions
+
+source("testFunctions") # utility test functions
+#------------------------------------------------------------------------------
+# infData example
+
+cat("Regresssion test cases\n")
+
+x <- c(15.6, 26.8,37.8,36.4,35.5,18.6,15.3,7.9,0.0)
+y <- c(5.2, 6.1, 8.7, 8.5, 8.8, 4.9, 4.5, 2.5, 1.1)
+model<-lm(y~x)
+coef <- coefficients(summary(model))
+intercept <- coef[1, 1]
+interceptStd <- coef[1, 2]
+slope <- coef[2, 1]
+slopeStd <- coef[2, 2]
+significance <- coef[2, 4]
+
+output <- "InfData std error test"
+if (assertEquals(0.011448491, slopeStd, tol, "Slope Standard Error") &&
+ assertEquals(0.286036932, interceptStd, tol, "Intercept Standard Error")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+output <- "InfData significance test"
+if (assertEquals(4.596e-07, significance, tol, "Significance")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+output <- "InfData conf interval test"
+ci<-confint(model)
+# ci[1,1] = lower 2.5% bound for intercept, ci[1,2] = upper 97.5% for intercept
+# ci[2,1] = lower 2.5% bound for slope, ci[2,2] = upper 97.5% for slope
+halfWidth <- ci[2,2] - slope
+if (assertEquals(0.0270713794287, halfWidth, tol,
+ "Slope conf. interval half-width")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+#------------------------------------------------------------------------------
+# Norris dataset from NIST examples
+
+y <- c(0.1, 338.8, 118.1, 888.0, 9.2, 228.1, 668.5, 998.5, 449.1, 778.9, 559.2,
+0.3, 0.1, 778.1, 668.8, 339.3, 448.9, 10.8, 557.7, 228.3, 998.0, 888.8, 119.6,
+0.3, 0.6, 557.6, 339.3, 888.0, 998.5, 778.9, 10.2, 117.6, 228.9, 668.4, 449.2,
+0.2)
+x <- c(0.2, 337.4, 118.2, 884.6, 10.1, 226.5, 666.3, 996.3, 448.6, 777.0, 558.2,
+0.4, 0.6, 775.5, 666.9, 338.0, 447.5, 11.6, 556.0, 228.1, 995.8, 887.6, 120.2,
+0.3, 0.3, 556.8, 339.1, 887.2, 999.0, 779.0, 11.1, 118.3, 229.2, 669.1, 448.9,
+0.5)
+model<-lm(y~x)
+coef <- coefficients(summary(model))
+intercept <- coef[1, 1]
+interceptStd <- coef[1, 2]
+slope <- coef[2, 1]
+slopeStd <- coef[2, 2]
+
+output <- "Norris std error test"
+if (assertEquals(0.429796848199937E-03, slopeStd, tol, "Slope Standard Error")
+ && assertEquals(0.232818234301152, interceptStd, tol,
+ "Intercept Standard Error")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+#------------------------------------------------------------------------------
+# infData2 -- bad fit example
+#
+x <- c(1,2,3,4,5,6)
+y <- c(1,0,5,2,-1,12)
+model<-lm(y~x)
+coef <- coefficients(summary(model))
+intercept <- coef[1, 1]
+interceptStd <- coef[1, 2]
+slope <- coef[2, 1]
+slopeStd <- coef[2, 2]
+significance <- coef[2, 4]
+
+output <- "InfData2 std error test"
+if (assertEquals(1.07260253, slopeStd, tol, "Slope Standard Error") &&
+ assertEquals(4.17718672, interceptStd, tol, "Intercept Standard Error")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+output <- "InfData2 significance test"
+if (assertEquals(0.261829133982, significance, tol, "Significance")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+output <- "InfData2 conf interval test"
+ci<-confint(model)
+# ci[1,1] = lower 2.5% bound for intercept, ci[1,2] = upper 97.5% for intercept
+# ci[2,1] = lower 2.5% bound for slope, ci[2,2] = upper 97.5% for slope
+halfWidth <- ci[2,2] - slope
+if (assertEquals(2.97802204827, halfWidth, tol,
+ "Slope conf. interval half-width")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+#------------------------------------------------------------------------------
+# Correlation example
+
+x <- c(101.0, 100.1, 100.0, 90.6, 86.5, 89.7, 90.6, 82.8, 70.1, 65.4,
+ 61.3, 62.5, 63.6, 52.6, 59.7, 59.5, 61.3)
+y <- c(99.2, 99.0, 100.0, 111.6, 122.2, 117.6, 121.1, 136.0, 154.2, 153.6,
+ 158.5, 140.6, 136.2, 168.0, 154.3, 149.0, 165.5)
+
+output <- "Correlation test"
+if (assertEquals(-0.94663767742, cor(x,y, method="pearson"), tol,
+ "Correlation coefficient")) {
+ displayPadded(output, SUCCEEDED, WIDTH)
+} else {
+ displayPadded(output, FAILED, WIDTH)
+}
+
+displayDashes(WIDTH)
+
+
+
+
diff --git a/src/test/R/testAll b/src/test/R/testAll
new file mode 100644
index 0000000..ba5d223
--- /dev/null
+++ b/src/test/R/testAll
@@ -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.
+#
+#------------------------------------------------------------------------------
+# R source file to run all commons-math R verification tests
+#
+# To run the test, install R, put this file and all other o.a.c.math R
+# verification tests and the testfunctions utilities file into the same
+# directory, launch R from this directory and then enter
+# source("<name-of-this-file>")
+#
+# To redirect output to a file, uncomment the following line, substituting
+# another file path if you like (default behavior is to write the file to the
+# current directory).
+#
+# sink("testResults")
+#------------------------------------------------------------------------------
+# distribution
+source("binomialTestCases")
+source("normalTestCases")
+source("poissonTestCases")
+source("hypergeometricTestCases")
+source("exponentialTestCases")
+source("cauchyTestCases.R")
+source("pascalTestCases")
+source("TDistributionTestCases.R")
+source("FDistributionTestCases.R")
+source("GammaDistributionTestCases.R")
+source("WeibullDistributionTestCases.R")
+source("ChiSquareDistributionTestCases.R")
+
+# regression
+source("regressionTestCases")
+
+# inference
+source("chiSquareTestCases")
+source("anovaTestCases")
+
+# descriptive
+source("descriptiveTestCases")
+
+# multiple regression
+source("multipleOLSRegressionTestCases")
+
+# covariance
+source("covarianceTestCases")
+
+# correlation
+source("correlationTestCases")
+
+#------------------------------------------------------------------------------
+# if output has been diverted, change it back
+if (sink.number()) {
+ sink()
+}
diff --git a/src/test/R/testFunctions b/src/test/R/testFunctions
new file mode 100644
index 0000000..c952774
--- /dev/null
+++ b/src/test/R/testFunctions
@@ -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.
+#
+#------------------------------------------------------------------------------
+#
+# Utility functions used in R comparison tests.
+#
+#------------------------------------------------------------------------------
+# Global constants
+#------------------------------------------------------------------------------
+WIDTH <- 80 # screen size constant for display functions
+SUCCEEDED <- "SUCCEEDED"
+FAILED <- "FAILED"
+options(digits=12) # display 12 digits throughout
+#------------------------------------------------------------------------------
+# Comparison functions
+#------------------------------------------------------------------------------
+# Tests to see if <expected> and <observed> are within <tol> of
+# one another in the sup norm.
+#
+# Returns 1 if no pair of corresponding non-NULL, non-NaN, non-na entries
+# differs by more than abs and NULLs, NaNs, na's correspond;
+# otherwise displays <message> and returns 0.
+# Works for both vectors and scalar values.
+#
+assertEquals <- function(expected, observed, tol, message) {
+ failed <- 0
+ if (any(is.na(observed) != is.na(expected))) {
+ failed <- 1
+ }
+ if (any(is.null(observed) != is.null(expected))) {
+ failed <- 1
+ }
+ if (any(is.nan(expected) != is.nan(observed))) {
+ failed <- 1
+ }
+ if (any(is.na(expected) != is.na(observed))) {
+ failed <- 1
+ }
+ if (!failed) {
+ if(any(abs(observed - expected) > tol, na.rm = TRUE)) {
+ failed <- 1
+ }
+ }
+ if (failed) {
+ cat("FAILURE: ",message,"\n")
+ cat("EXPECTED: ",expected,"\n")
+ cat("OBSERVED: ",observed,"\n")
+ cat("DIFF: ",observed - expected,"\n")
+ cat("TOLERANCE: ",tol,"\n")
+ }
+ return(!failed)
+}
+#------------------------------------------------------------------------------
+# Display functions
+#------------------------------------------------------------------------------
+# Displays n-col dashed line.
+#
+displayDashes <- function(n) {
+ cat(rep("-",n),"\n",sep='')
+ return(1)
+}
+#------------------------------------------------------------------------------
+# Displays <start>......<end> with enough dots in between to make <n> cols,
+# followed by a new line character. Blows up if <start><end> is longer than
+# <n> cols by itself.
+#
+# Expects <start> and <end> to be strings (character vectors).
+#
+displayPadded <- function(start, end, n) {
+ len = sum(nchar(start)) + sum(nchar(end))
+ cat(start, rep(".", n - len), end, "\n",sep='')
+ return(1)
+}
diff --git a/src/test/java/org/apache/commons/math/ArgumentOutsideDomainExceptionTest.java b/src/test/java/org/apache/commons/math/ArgumentOutsideDomainExceptionTest.java
new file mode 100644
index 0000000..2702363
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ArgumentOutsideDomainExceptionTest.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;
+
+import java.util.Locale;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class ArgumentOutsideDomainExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ ArgumentOutsideDomainException ex = new ArgumentOutsideDomainException(FastMath.PI, 10.0, 20.0);
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("3.14") > 0);
+ assertEquals(FastMath.PI, ex.getArgument()[0], 0);
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ConvergenceExceptionTest.java b/src/test/java/org/apache/commons/math/ConvergenceExceptionTest.java
new file mode 100644
index 0000000..ea8e478
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ConvergenceExceptionTest.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;
+
+import junit.framework.TestCase;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * @version $Revision: 1035475 $ $Date: 2010-11-15 23:39:25 +0100 (lun. 15 nov. 2010) $
+ */
+public class ConvergenceExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ ConvergenceException ex = new ConvergenceException();
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertNotNull(ex.getMessage(Locale.FRENCH));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorPatternArguments(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ ConvergenceException ex = new ConvergenceException(pattern, arguments);
+ assertNull(ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorCause(){
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ ConvergenceException ex = new ConvergenceException(cause);
+ assertEquals(cause, ex.getCause());
+ }
+
+ public void testConstructorPatternArgumentsCause(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ ConvergenceException ex = new ConvergenceException(cause, pattern, arguments);
+ assertEquals(cause, ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/DuplicateSampleAbscissaExceptionTest.java b/src/test/java/org/apache/commons/math/DuplicateSampleAbscissaExceptionTest.java
new file mode 100644
index 0000000..3555283
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/DuplicateSampleAbscissaExceptionTest.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;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class DuplicateSampleAbscissaExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ DuplicateSampleAbscissaException ex = new DuplicateSampleAbscissaException(1.2, 10, 11);
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("1.2") > 0);
+ assertEquals(1.2, ex.getDuplicateAbscissa(), 0);
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/FunctionEvaluationExceptionTest.java b/src/test/java/org/apache/commons/math/FunctionEvaluationExceptionTest.java
new file mode 100644
index 0000000..0d2e161
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/FunctionEvaluationExceptionTest.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;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ * @deprecated in 2.2 (to be removed in 3.0)
+ */
+ at Deprecated
+public class FunctionEvaluationExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ FunctionEvaluationException ex = new FunctionEvaluationException(0.0);
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("0") > 0);
+ assertEquals(0.0, ex.getArgument()[0], 0);
+ }
+
+ public void testConstructorArray(){
+ FunctionEvaluationException ex =
+ new FunctionEvaluationException(new double[] { 0, 1, 2 });
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("0") > 0);
+ assertEquals(0.0, ex.getArgument()[0], 0);
+ assertEquals(1.0, ex.getArgument()[1], 0);
+ assertEquals(2.0, ex.getArgument()[2], 0);
+ }
+
+ public void testConstructorPatternArguments(){
+ LocalizedFormats pattern = LocalizedFormats.EVALUATION_FAILED;
+ Object[] arguments = { Double.valueOf(0.0) };
+ FunctionEvaluationException ex = new FunctionEvaluationException(0.0, pattern, arguments);
+ assertNull(ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorArrayPatternArguments(){
+ LocalizedFormats pattern = LocalizedFormats.EVALUATION_FAILED;
+ Object[] arguments = { Double.valueOf(0.0) };
+ FunctionEvaluationException ex =
+ new FunctionEvaluationException(new double[] { 0, 1, 2 }, pattern, arguments);
+ assertNull(ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ assertEquals(0.0, ex.getArgument()[0], 0);
+ assertEquals(1.0, ex.getArgument()[1], 0);
+ assertEquals(2.0, ex.getArgument()[2], 0);
+ }
+
+ public void testConstructorPatternArgumentsCause(){
+ LocalizedFormats pattern = LocalizedFormats.EVALUATION_FAILED;
+ Object[] arguments = { Double.valueOf(0.0) };
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ FunctionEvaluationException ex = new FunctionEvaluationException(cause, 0.0, pattern, arguments);
+ assertEquals(cause, ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorArrayPatternArgumentsCause(){
+ LocalizedFormats pattern = LocalizedFormats.EVALUATION_FAILED;
+ Object[] arguments = { Double.valueOf(0.0) };
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ FunctionEvaluationException ex =
+ new FunctionEvaluationException(cause, new double[] { 0, 1, 2 }, pattern, arguments);
+ assertEquals(cause, ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ assertEquals(0.0, ex.getArgument()[0], 0);
+ assertEquals(1.0, ex.getArgument()[1], 0);
+ assertEquals(2.0, ex.getArgument()[2], 0);
+ }
+
+ public void testConstructorArgumentCause(){
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ FunctionEvaluationException ex = new FunctionEvaluationException(cause, 0.0);
+ assertEquals(cause, ex.getCause());
+ assertTrue(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorArrayArgumentCause(){
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ FunctionEvaluationException ex =
+ new FunctionEvaluationException(cause, new double[] { 0, 1, 2 });
+ assertEquals(cause, ex.getCause());
+ assertTrue(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ assertEquals(0.0, ex.getArgument()[0], 0);
+ assertEquals(1.0, ex.getArgument()[1], 0);
+ assertEquals(2.0, ex.getArgument()[2], 0);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/MathConfigurationExceptionTest.java b/src/test/java/org/apache/commons/math/MathConfigurationExceptionTest.java
new file mode 100644
index 0000000..3fd3226
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/MathConfigurationExceptionTest.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;
+
+import junit.framework.TestCase;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * @version $Revision: 1035475 $ $Date: 2010-11-15 23:39:25 +0100 (lun. 15 nov. 2010) $
+ */
+public class MathConfigurationExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ MathConfigurationException ex = new MathConfigurationException();
+ assertNull(ex.getCause());
+ assertEquals("", ex.getMessage());
+ assertEquals("", ex.getMessage(Locale.FRENCH));
+ }
+
+ public void testConstructorPatternArguments(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ MathConfigurationException ex = new MathConfigurationException(pattern, arguments);
+ assertNull(ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorCause(){
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ MathConfigurationException ex = new MathConfigurationException(cause);
+ assertEquals(cause, ex.getCause());
+ }
+
+ public void testConstructorPatternArgumentsCause(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ MathConfigurationException ex = new MathConfigurationException(cause, pattern, arguments);
+ assertEquals(cause, ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/MathExceptionTest.java b/src/test/java/org/apache/commons/math/MathExceptionTest.java
new file mode 100644
index 0000000..7f6a092
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/MathExceptionTest.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;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.DummyLocalizable;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * @version $Revision: 1035475 $ $Date: 2010-11-15 23:39:25 +0100 (lun. 15 nov. 2010) $
+ */
+public class MathExceptionTest extends TestCase {
+
+ public void testConstructor(){
+ MathException ex = new MathException();
+ assertNull(ex.getCause());
+ assertEquals("", ex.getMessage());
+ assertEquals("", ex.getMessage(Locale.FRENCH));
+ }
+
+ public void testConstructorPatternArguments(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ MathException ex = new MathException(pattern, arguments);
+ assertNull(ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testConstructorCause(){
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ MathException ex = new MathException(cause);
+ assertEquals(cause, ex.getCause());
+ }
+
+ public void testConstructorPatternArgumentsCause(){
+ LocalizedFormats pattern = LocalizedFormats.ROTATION_MATRIX_DIMENSIONS;
+ Object[] arguments = { Integer.valueOf(6), Integer.valueOf(4) };
+ String inMsg = "inner message";
+ Exception cause = new Exception(inMsg);
+ MathException ex = new MathException(cause, pattern, arguments);
+ assertEquals(cause, ex.getCause());
+ assertEquals(pattern, ex.getGeneralPattern());
+ assertEquals(arguments.length, ex.getArguments().length);
+ for (int i = 0; i < arguments.length; ++i) {
+ assertEquals(arguments[i], ex.getArguments()[i]);
+ }
+ assertFalse(pattern.equals(ex.getMessage()));
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ /**
+ * Tests the printStackTrace() operation.
+ */
+ public void testPrintStackTrace() {
+ Localizable outMsg = new DummyLocalizable("outer message");
+ Localizable inMsg = new DummyLocalizable("inner message");
+ MathException cause = new MathConfigurationException(inMsg);
+ MathException ex = new MathException(cause, outMsg);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ ex.printStackTrace(ps);
+ String stack = baos.toString();
+ String outerMsg = "org.apache.commons.math.MathException: outer message";
+ String innerMsg = "Caused by: " +
+ "org.apache.commons.math.MathConfigurationException: inner message";
+ assertTrue(stack.startsWith(outerMsg));
+ assertTrue(stack.indexOf(innerMsg) > 0);
+
+ PrintWriter pw = new PrintWriter(ps, true);
+ ex.printStackTrace(pw);
+ stack = baos.toString();
+ assertTrue(stack.startsWith(outerMsg));
+ assertTrue(stack.indexOf(innerMsg) > 0);
+ }
+
+ /**
+ * Test serialization
+ */
+ public void testSerialization() {
+ Localizable outMsg = new DummyLocalizable("outer message");
+ Localizable inMsg = new DummyLocalizable("inner message");
+ MathException cause = new MathConfigurationException(inMsg);
+ MathException ex = new MathException(cause, outMsg);
+ MathException image = (MathException) TestUtils.serializeAndRecover(ex);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ ex.printStackTrace(ps);
+ String stack = baos.toString();
+
+ ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+ PrintStream ps2 = new PrintStream(baos2);
+ image.printStackTrace(ps2);
+ String stack2 = baos2.toString();
+
+ // See if JDK supports nested exceptions. If not, stack trace of
+ // inner exception will not be serialized
+ boolean jdkSupportsNesting = false;
+ try {
+ Throwable.class.getDeclaredMethod("getCause", new Class[0]);
+ jdkSupportsNesting = true;
+ } catch (NoSuchMethodException e) {
+ jdkSupportsNesting = false;
+ }
+
+ if (jdkSupportsNesting) {
+ assertEquals(stack, stack2);
+ } else {
+ assertTrue(stack2.indexOf(inMsg.getSourceString()) != -1);
+ assertTrue(stack2.indexOf("MathConfigurationException") != -1);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/MaxIterationsExceededExceptionTest.java b/src/test/java/org/apache/commons/math/MaxIterationsExceededExceptionTest.java
new file mode 100644
index 0000000..74df44d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/MaxIterationsExceededExceptionTest.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;
+
+import java.util.Locale;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class MaxIterationsExceededExceptionTest extends TestCase {
+
+ public void testSimpleConstructor(){
+ MaxIterationsExceededException ex = new MaxIterationsExceededException(1000000);
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("1,000,000") > 0);
+ assertEquals(1000000, ex.getMaxIterations());
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+ public void testComplexConstructor(){
+ MaxIterationsExceededException ex =
+ new MaxIterationsExceededException(1000000,
+ LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION,
+ 1234567);
+ assertNull(ex.getCause());
+ assertNotNull(ex.getMessage());
+ assertTrue(ex.getMessage().indexOf("1,000,000") < 0);
+ assertTrue(ex.getMessage().indexOf("1,234,567") > 0);
+ assertEquals(1000000, ex.getMaxIterations());
+ assertFalse(ex.getMessage().equals(ex.getMessage(Locale.FRENCH)));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/RetryTestCase.java b/src/test/java/org/apache/commons/math/RetryTestCase.java
new file mode 100644
index 0000000..7092d85
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/RetryTestCase.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;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * A TestCase that retries tests when assertions fail.
+ * <p>
+ * If one or more tests throw an AssertionFailedError, all tests are
+ * repeated one time.
+ * <p>
+ * Errors or exceptions other than AssertionFailedError do not lead to retries.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public abstract class RetryTestCase extends TestCase {
+
+ public RetryTestCase() {
+ super();
+ }
+
+ public RetryTestCase(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * Override runTest() to catch AssertionFailedError and retry
+ */
+ @Override
+ protected void runTest() throws Throwable {
+ try {
+ super.runTest();
+ } catch (AssertionFailedError err) {
+ // System.out.println("Retrying " + this.getName());
+ super.runTest();
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/TestUtils.java b/src/test/java/org/apache/commons/math/TestUtils.java
new file mode 100644
index 0000000..64137cf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/TestUtils.java
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.DecimalFormat;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.complex.ComplexFormat;
+import org.apache.commons.math.distribution.ContinuousDistribution;
+import org.apache.commons.math.linear.FieldMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.stat.inference.ChiSquareTest;
+import org.apache.commons.math.stat.inference.ChiSquareTestImpl;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
+ */
+public class TestUtils {
+ /**
+ * Collection of static methods used in math unit tests.
+ */
+ private TestUtils() {
+ super();
+ }
+
+ /**
+ * Verifies that expected and actual are within delta, or are both NaN or
+ * infinities of the same sign.
+ */
+ public static void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+
+ /**
+ * Verifies that expected and actual are within delta, or are both NaN or
+ * infinities of the same sign.
+ */
+ public static void assertEquals(String msg, double expected, double actual, double delta) {
+ // check for NaN
+ if(Double.isNaN(expected)){
+ Assert.assertTrue("" + actual + " is not NaN.",
+ Double.isNaN(actual));
+ } else {
+ Assert.assertEquals(msg, expected, actual, delta);
+ }
+ }
+
+ /**
+ * Verifies that the two arguments are exactly the same, either
+ * both NaN or infinities of same sign, or identical floating point values.
+ */
+ public static void assertSame(double expected, double actual) {
+ assertEquals(expected, actual, 0);
+ }
+
+ /**
+ * Verifies that real and imaginary parts of the two complex arguments
+ * are exactly the same. Also ensures that NaN / infinite components match.
+ */
+ public static void assertSame(Complex expected, Complex actual) {
+ assertSame(expected.getReal(), actual.getReal());
+ assertSame(expected.getImaginary(), actual.getImaginary());
+ }
+
+ /**
+ * Verifies that real and imaginary parts of the two complex arguments
+ * differ by at most delta. Also ensures that NaN / infinite components match.
+ */
+ public static void assertEquals(Complex expected, Complex actual, double delta) {
+ assertEquals(expected.getReal(), actual.getReal(), delta);
+ assertEquals(expected.getImaginary(), actual.getImaginary(), delta);
+ }
+
+ /**
+ * Verifies that two double arrays have equal entries, up to tolerance
+ */
+ public static void assertEquals(double expected[], double observed[], double tolerance) {
+ assertEquals("Array comparison failure", expected, observed, tolerance);
+ }
+
+ /**
+ * Serializes an object to a bytes array and then recovers the object from the bytes array.
+ * Returns the deserialized object.
+ *
+ * @param o object to serialize and recover
+ * @return the recovered, deserialized object
+ */
+ public static Object serializeAndRecover(Object o) {
+ try {
+ // serialize the Object
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream so = new ObjectOutputStream(bos);
+ so.writeObject(o);
+
+ // deserialize the Object
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream si = new ObjectInputStream(bis);
+ return si.readObject();
+ } catch (IOException ioe) {
+ return null;
+ } catch (ClassNotFoundException cnfe) {
+ return null;
+ }
+ }
+
+ /**
+ * Verifies that serialization preserves equals and hashCode.
+ * Serializes the object, then recovers it and checks equals and hash code.
+ *
+ * @param object the object to serialize and recover
+ */
+ public static void checkSerializedEquality(Object object) {
+ Object object2 = serializeAndRecover(object);
+ Assert.assertEquals("Equals check", object, object2);
+ Assert.assertEquals("HashCode check", object.hashCode(), object2.hashCode());
+ }
+
+ /**
+ * Verifies that the relative error in actual vs. expected is less than or
+ * equal to relativeError. If expected is infinite or NaN, actual must be
+ * the same (NaN or infinity of the same sign).
+ *
+ * @param expected expected value
+ * @param actual observed value
+ * @param relativeError maximum allowable relative error
+ */
+ public static void assertRelativelyEquals(double expected, double actual,
+ double relativeError) {
+ assertRelativelyEquals(null, expected, actual, relativeError);
+ }
+
+ /**
+ * Verifies that the relative error in actual vs. expected is less than or
+ * equal to relativeError. If expected is infinite or NaN, actual must be
+ * the same (NaN or infinity of the same sign).
+ *
+ * @param msg message to return with failure
+ * @param expected expected value
+ * @param actual observed value
+ * @param relativeError maximum allowable relative error
+ */
+ public static void assertRelativelyEquals(String msg, double expected,
+ double actual, double relativeError) {
+ if (Double.isNaN(expected)) {
+ Assert.assertTrue(msg, Double.isNaN(actual));
+ } else if (Double.isNaN(actual)) {
+ Assert.assertTrue(msg, Double.isNaN(expected));
+ } else if (Double.isInfinite(actual) || Double.isInfinite(expected)) {
+ Assert.assertEquals(expected, actual, relativeError);
+ } else if (expected == 0.0) {
+ Assert.assertEquals(msg, actual, expected, relativeError);
+ } else {
+ double absError = FastMath.abs(expected) * relativeError;
+ Assert.assertEquals(msg, expected, actual, absError);
+ }
+ }
+
+ /**
+ * Fails iff values does not contain a number within epsilon of z.
+ *
+ * @param msg message to return with failure
+ * @param values complex array to search
+ * @param z value sought
+ * @param epsilon tolerance
+ */
+ public static void assertContains(String msg, Complex[] values,
+ Complex z, double epsilon) {
+ int i = 0;
+ boolean found = false;
+ while (!found && i < values.length) {
+ try {
+ assertEquals(values[i], z, epsilon);
+ found = true;
+ } catch (AssertionFailedError er) {
+ // no match
+ }
+ i++;
+ }
+ if (!found) {
+ Assert.fail(msg +
+ " Unable to find " + ComplexFormat.formatComplex(z));
+ }
+ }
+
+ /**
+ * Fails iff values does not contain a number within epsilon of z.
+ *
+ * @param values complex array to search
+ * @param z value sought
+ * @param epsilon tolerance
+ */
+ public static void assertContains(Complex[] values,
+ Complex z, double epsilon) {
+ assertContains(null, values, z, epsilon);
+ }
+
+ /**
+ * Fails iff values does not contain a number within epsilon of x.
+ *
+ * @param msg message to return with failure
+ * @param values double array to search
+ * @param x value sought
+ * @param epsilon tolerance
+ */
+ public static void assertContains(String msg, double[] values,
+ double x, double epsilon) {
+ int i = 0;
+ boolean found = false;
+ while (!found && i < values.length) {
+ try {
+ assertEquals(values[i], x, epsilon);
+ found = true;
+ } catch (AssertionFailedError er) {
+ // no match
+ }
+ i++;
+ }
+ if (!found) {
+ Assert.fail(msg + " Unable to find" + x);
+ }
+ }
+
+ /**
+ * Fails iff values does not contain a number within epsilon of x.
+ *
+ * @param values double array to search
+ * @param x value sought
+ * @param epsilon tolerance
+ */
+ public static void assertContains(double[] values, double x,
+ double epsilon) {
+ assertContains(null, values, x, epsilon);
+ }
+
+ /** verifies that two matrices are close (1-norm) */
+ public static void assertEquals(String msg, RealMatrix expected, RealMatrix observed,
+ double tolerance) {
+
+ Assert.assertNotNull(msg + "\nObserved should not be null",observed);
+
+ if (expected.getColumnDimension() != observed.getColumnDimension() ||
+ expected.getRowDimension() != observed.getRowDimension()) {
+ StringBuilder messageBuffer = new StringBuilder(msg);
+ messageBuffer.append("\nObserved has incorrect dimensions.");
+ messageBuffer.append("\nobserved is " + observed.getRowDimension() +
+ " x " + observed.getColumnDimension());
+ messageBuffer.append("\nexpected " + expected.getRowDimension() +
+ " x " + expected.getColumnDimension());
+ Assert.fail(messageBuffer.toString());
+ }
+
+ RealMatrix delta = expected.subtract(observed);
+ if (delta.getNorm() >= tolerance) {
+ StringBuilder messageBuffer = new StringBuilder(msg);
+ messageBuffer.append("\nExpected: " + expected);
+ messageBuffer.append("\nObserved: " + observed);
+ messageBuffer.append("\nexpected - observed: " + delta);
+ Assert.fail(messageBuffer.toString());
+ }
+ }
+
+ /** verifies that two matrices are equal */
+ public static void assertEquals(FieldMatrix<? extends FieldElement<?>> expected,
+ FieldMatrix<? extends FieldElement<?>> observed) {
+
+ Assert.assertNotNull("Observed should not be null",observed);
+
+ if (expected.getColumnDimension() != observed.getColumnDimension() ||
+ expected.getRowDimension() != observed.getRowDimension()) {
+ StringBuilder messageBuffer = new StringBuilder();
+ messageBuffer.append("Observed has incorrect dimensions.");
+ messageBuffer.append("\nobserved is " + observed.getRowDimension() +
+ " x " + observed.getColumnDimension());
+ messageBuffer.append("\nexpected " + expected.getRowDimension() +
+ " x " + expected.getColumnDimension());
+ Assert.fail(messageBuffer.toString());
+ }
+
+ for (int i = 0; i < expected.getRowDimension(); ++i) {
+ for (int j = 0; j < expected.getColumnDimension(); ++j) {
+ FieldElement<?> eij = expected.getEntry(i, j);
+ FieldElement<?> oij = observed.getEntry(i, j);
+ Assert.assertEquals(eij, oij);
+ }
+ }
+ }
+
+ /** verifies that two arrays are close (sup norm) */
+ public static void assertEquals(String msg, double[] expected, double[] observed,
+ double tolerance) {
+ StringBuilder out = new StringBuilder(msg);
+ if (expected.length != observed.length) {
+ out.append("\n Arrays not same length. \n");
+ out.append("expected has length ");
+ out.append(expected.length);
+ out.append(" observed length = ");
+ out.append(observed.length);
+ Assert.fail(out.toString());
+ }
+ boolean failure = false;
+ for (int i=0; i < expected.length; i++) {
+ try {
+ assertEquals(expected[i], observed[i], tolerance);
+ } catch (AssertionFailedError ex) {
+ failure = true;
+ out.append("\n Elements at index ");
+ out.append(i);
+ out.append(" differ. ");
+ out.append(" expected = ");
+ out.append(expected[i]);
+ out.append(" observed = ");
+ out.append(observed[i]);
+ }
+ }
+ if (failure) {
+ Assert.fail(out.toString());
+ }
+ }
+
+ /** verifies that two arrays are equal */
+ public static <T extends FieldElement<T>> void assertEquals(T[] m, T[] n) {
+ if (m.length != n.length) {
+ Assert.fail("vectors not same length");
+ }
+ for (int i = 0; i < m.length; i++) {
+ Assert.assertEquals(m[i],n[i]);
+ }
+ }
+
+ /**
+ * Computes the sum of squared deviations of <values> from <target>
+ * @param values array of deviates
+ * @param target value to compute deviations from
+ *
+ * @return sum of squared deviations
+ */
+ public static double sumSquareDev(double[] values, double target) {
+ double sumsq = 0d;
+ for (int i = 0; i < values.length; i++) {
+ final double dev = values[i] - target;
+ sumsq += (dev * dev);
+ }
+ return sumsq;
+ }
+
+ /**
+ * Asserts the null hypothesis for a ChiSquare test. Fails and dumps arguments and test
+ * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
+ *
+ * @param valueLabels
+ * @param expected expected counts
+ * @param observed observed counts
+ * @param alpha significance level of the test
+ */
+ public static void assertChiSquareAccept(String[] valueLabels, double[] expected, long[] observed, double alpha) throws Exception {
+ ChiSquareTest chiSquareTest = new ChiSquareTestImpl();
+ try {
+ // Fail if we can reject null hypothesis that distributions are the same
+ Assert.assertFalse(chiSquareTest.chiSquareTest(expected, observed, alpha));
+ } catch (AssertionFailedError ex) {
+ StringBuilder msgBuffer = new StringBuilder();
+ DecimalFormat df = new DecimalFormat("#.##");
+ msgBuffer.append("Chisquare test failed");
+ msgBuffer.append(" p-value = ");
+ msgBuffer.append(chiSquareTest.chiSquareTest(expected, observed));
+ msgBuffer.append(" chisquare statistic = ");
+ msgBuffer.append(chiSquareTest.chiSquare(expected, observed));
+ msgBuffer.append(". \n");
+ msgBuffer.append("value\texpected\tobserved\n");
+ for (int i = 0; i < expected.length; i++) {
+ msgBuffer.append(valueLabels[i]);
+ msgBuffer.append("\t");
+ msgBuffer.append(df.format(expected[i]));
+ msgBuffer.append("\t\t");
+ msgBuffer.append(observed[i]);
+ msgBuffer.append("\n");
+ }
+ msgBuffer.append("This test can fail randomly due to sampling error with probability ");
+ msgBuffer.append(alpha);
+ msgBuffer.append(".");
+ Assert.fail(msgBuffer.toString());
+ }
+ }
+
+ /**
+ * Asserts the null hypothesis for a ChiSquare test. Fails and dumps arguments and test
+ * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
+ *
+ * @param values
+ * @param expected expected counts
+ * @param observed observed counts
+ * @param alpha significance level of the test
+ */
+ public static void assertChiSquareAccept(int[] values, double[] expected, long[] observed, double alpha) throws Exception {
+ String[] labels = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ labels[i] = Integer.toString(values[i]);
+ }
+ assertChiSquareAccept(labels, expected, observed, alpha);
+ }
+
+ /**
+ * Asserts the null hypothesis for a ChiSquare test. Fails and dumps arguments and test
+ * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
+ *
+ * @param values
+ * @param expected expected counts
+ * @param observed observed counts
+ * @param alpha significance level of the test
+ */
+ public static void assertChiSquareAccept(double[] values, double[] expected, long[] observed, double alpha) throws Exception {
+ String[] labels = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ labels[i] = Double.toString(values[i]);
+ }
+ assertChiSquareAccept(labels, expected, observed, alpha);
+ }
+
+ /**
+ * Asserts the null hypothesis for a ChiSquare test. Fails and dumps arguments and test
+ * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
+ *
+ * @param expected expected counts
+ * @param observed observed counts
+ * @param alpha significance level of the test
+ */
+ public static void assertChiSquareAccept(double[] expected, long[] observed, double alpha) throws Exception {
+ String[] labels = new String[expected.length];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = Integer.toString(i + 1);
+ }
+ assertChiSquareAccept(labels, expected, observed, alpha);
+ }
+
+ /**
+ * Computes the 25th, 50th and 75th percentiles of the given distribution and returns
+ * these values in an array.
+ */
+ public static double[] getDistributionQuartiles(ContinuousDistribution distribution) throws Exception {
+ double[] quantiles = new double[3];
+ quantiles[0] = distribution.inverseCumulativeProbability(0.25d);
+ quantiles[1] = distribution.inverseCumulativeProbability(0.5d);
+ quantiles[2] = distribution.inverseCumulativeProbability(0.75d);
+ return quantiles;
+ }
+
+ /**
+ * Updates observed counts of values in quartiles.
+ * counts[0] <-> 1st quartile ... counts[3] <-> top quartile
+ */
+ public static void updateCounts(double value, long[] counts, double[] quartiles) {
+ if (value < quartiles[0]) {
+ counts[0]++;
+ } else if (value > quartiles[2]) {
+ counts[3]++;
+ } else if (value > quartiles[1]) {
+ counts[2]++;
+ } else {
+ counts[1]++;
+ }
+ }
+
+ /**
+ * Eliminates points with zero mass from densityPoints and densityValues parallel
+ * arrays. Returns the number of positive mass points and collapses the arrays so
+ * that the first <returned value> elements of the input arrays represent the positive
+ * mass points.
+ */
+ public static int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) {
+ int positiveMassCount = 0;
+ for (int i = 0; i < densityValues.length; i++) {
+ if (densityValues[i] > 0) {
+ positiveMassCount++;
+ }
+ }
+ if (positiveMassCount < densityValues.length) {
+ int[] newPoints = new int[positiveMassCount];
+ double[] newValues = new double[positiveMassCount];
+ int j = 0;
+ for (int i = 0; i < densityValues.length; i++) {
+ if (densityValues[i] > 0) {
+ newPoints[j] = densityPoints[i];
+ newValues[j] = densityValues[i];
+ j++;
+ }
+ }
+ System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount);
+ System.arraycopy(newValues,0,densityValues,0,positiveMassCount);
+ }
+ return positiveMassCount;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/BinaryFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/BinaryFunctionTest.java
new file mode 100644
index 0000000..2311a2e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/BinaryFunctionTest.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.analysis;
+
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BinaryFunctionTest {
+
+ @Test
+ public void testAdd() throws Exception {
+ Assert.assertEquals(5.0, BinaryFunction.ADD.value(2, 3), 1.0e-15);
+ Assert.assertEquals(0.0, BinaryFunction.ADD.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testSubtract() throws Exception {
+ Assert.assertEquals(-1.0, BinaryFunction.SUBTRACT.value(2, 3), 1.0e-15);
+ Assert.assertEquals(-2.0, BinaryFunction.SUBTRACT.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testMultiply() throws Exception {
+ Assert.assertEquals(6.0, BinaryFunction.MULTIPLY.value(2, 3), 1.0e-15);
+ Assert.assertEquals(-1.0, BinaryFunction.MULTIPLY.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testDivide() throws Exception {
+ Assert.assertEquals(1.5, BinaryFunction.DIVIDE.value(3, 2), 1.0e-15);
+ Assert.assertEquals(-1.0, BinaryFunction.DIVIDE.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testPow() throws Exception {
+ Assert.assertEquals(9.0, BinaryFunction.POW.value(3, 2), 1.0e-15);
+ Assert.assertEquals(-1.0, BinaryFunction.POW.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testAtan2() throws Exception {
+ Assert.assertEquals(FastMath.PI / 4, BinaryFunction.ATAN2.value(1, 1), 1.0e-15);
+ Assert.assertEquals(-FastMath.PI / 4, BinaryFunction.ATAN2.value(-1, 1), 1.0e-15);
+ }
+
+ @Test
+ public void testFix1st() throws Exception {
+ ComposableFunction f = BinaryFunction.POW.fix1stArgument(2);
+ for (double x = 0.0; x < 1.0; x += 0.01) {
+ Assert.assertEquals(FastMath.pow(2.0, x), f.value(x), 1.0e-15);
+ }
+ }
+
+ @Test
+ public void testFix2nd() throws Exception {
+ ComposableFunction f = BinaryFunction.POW.fix2ndArgument(2);
+ for (double y = 0.0; y < 1.0; y += 0.01) {
+ Assert.assertEquals(y * y, f.value(y), 1.0e-15);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/ComposableFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/ComposableFunctionTest.java
new file mode 100644
index 0000000..2099dce
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/ComposableFunctionTest.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.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ComposableFunctionTest {
+
+ @Test
+ public void testZero() throws Exception {
+ Assert.assertEquals(0.0, ComposableFunction.ZERO.value(1), 1.0e-15);
+ Assert.assertEquals(0.0, ComposableFunction.ZERO.value(2), 1.0e-15);
+ }
+
+ @Test
+ public void testOne() throws Exception {
+ Assert.assertEquals(1.0, ComposableFunction.ONE.value(1), 1.0e-15);
+ Assert.assertEquals(1.0, ComposableFunction.ONE.value(2), 1.0e-15);
+ }
+
+ @Test
+ public void testIdentity() throws Exception {
+ Assert.assertEquals(1.0, ComposableFunction.IDENTITY.value(1), 1.0e-15);
+ Assert.assertEquals(2.0, ComposableFunction.IDENTITY.value(2), 1.0e-15);
+ }
+
+ @Test
+ public void testRint() throws Exception {
+ Assert.assertEquals(1.0, ComposableFunction.RINT.value(0.9), 1.0e-15);
+ Assert.assertEquals(2.0, ComposableFunction.RINT.value(2.2), 1.0e-15);
+ }
+
+ @Test
+ public void testSignum() throws Exception {
+ Assert.assertEquals(1.0, ComposableFunction.SIGNUM.value(12.3), 1.0e-15);
+ Assert.assertEquals(-1.0, ComposableFunction.SIGNUM.value(-6), 1.0e-15);
+ }
+
+ @Test
+ public void testComposition() throws Exception {
+ ComposableFunction abs = ComposableFunction.ABS;
+ ComposableFunction acos = ComposableFunction.ACOS;
+ ComposableFunction asin = ComposableFunction.ASIN;
+ ComposableFunction atan = ComposableFunction.ATAN;
+ ComposableFunction cbrt = ComposableFunction.CBRT;
+ ComposableFunction ceil = ComposableFunction.CEIL;
+ ComposableFunction cos = ComposableFunction.COS;
+ ComposableFunction cosh = ComposableFunction.COSH;
+ ComposableFunction exp = ComposableFunction.EXP;
+ ComposableFunction expm1 = ComposableFunction.EXPM1;
+ ComposableFunction floor = ComposableFunction.FLOOR;
+ ComposableFunction id = ComposableFunction.IDENTITY;
+ ComposableFunction log = ComposableFunction.LOG;
+ ComposableFunction log10 = ComposableFunction.LOG10;
+ ComposableFunction negate = ComposableFunction.NEGATE;
+ ComposableFunction sin = ComposableFunction.SIN;
+ ComposableFunction sinh = ComposableFunction.SINH;
+ ComposableFunction sqrt = ComposableFunction.SQRT;
+ ComposableFunction tan = ComposableFunction.TAN;
+ ComposableFunction tanh = ComposableFunction.TANH;
+ ComposableFunction ulp = ComposableFunction.ULP;
+
+ ComposableFunction f1 = sqrt.of(abs.of(expm1.of(cbrt.of(tanh).of(id))));
+ for (double x = 0.1; x < 0.9; x += 0.01) {
+ Assert.assertEquals(FastMath.sqrt(FastMath.abs(FastMath.expm1(FastMath.cbrt(FastMath.tanh(x))))),
+ f1.value(x), 1.0e-15);
+ }
+
+ ComposableFunction f2 = cosh.of(sinh.of(tanh.of(ceil.postCompose(log.postCompose(cosh)))));
+ for (double x = 0.1; x < 12.9; x += 1.0) {
+ Assert.assertEquals(FastMath.cosh(FastMath.sinh(FastMath.tanh(FastMath.cosh(FastMath.log(FastMath.ceil(x)))))),
+ f2.value(x), 1.0e-15);
+ }
+
+ ComposableFunction f3 = cos.of(sin.of(tan.of(acos.of(asin.of(log10.of(log.of(ulp)))))));
+ for (double x = 1.0e16; x < 1.0e17; x += 1.0e16) {
+ Assert.assertEquals(FastMath.cos(FastMath.sin(FastMath.tan(FastMath.acos(FastMath.asin(FastMath.log10(FastMath.log(FastMath.ulp(x)))))))),
+ f3.value(x), 1.0e-15);
+ }
+
+ ComposableFunction f4 = atan.of(exp.of(negate.of(floor)));
+ for (double x = 1.1; x < 10.2; x += 1.0) {
+ Assert.assertEquals(FastMath.atan(FastMath.exp(-FastMath.floor(x))),
+ f4.value(x), 1.0e-15);
+ }
+
+ }
+
+ @Test
+ public void testCombine() throws Exception {
+
+ ComposableFunction f =
+ ComposableFunction.COS.combine(ComposableFunction.ASIN, BinaryFunction.POW);
+ for (double x = 0.1; x < 0.9; x += 0.01) {
+ Assert.assertEquals(FastMath.pow(FastMath.cos(x), FastMath.asin(x)), f.value(x), 1.0e-15);
+ }
+
+ }
+
+ @Test
+ public void testSimpleCombination() throws Exception {
+
+ ComposableFunction f1 = ComposableFunction.COS.add(3);
+ ComposableFunction f2 = ComposableFunction.COS.add(ComposableFunction.SIN);
+ ComposableFunction f3 = ComposableFunction.COS.subtract(ComposableFunction.SIN);
+ ComposableFunction f4 = ComposableFunction.COS.multiply(ComposableFunction.SIN);
+ ComposableFunction f5 = ComposableFunction.COS.multiply(5);
+ ComposableFunction f6 = ComposableFunction.COS.divide(ComposableFunction.SIN);
+ for (double x = 0.1; x < 0.9; x += 0.01) {
+ Assert.assertEquals(FastMath.cos(x) + 3, f1.value(x), 1.0e-15);
+ Assert.assertEquals(FastMath.cos(x) + FastMath.sin(x), f2.value(x), 1.0e-15);
+ Assert.assertEquals(FastMath.cos(x) - FastMath.sin(x), f3.value(x), 1.0e-15);
+ Assert.assertEquals(FastMath.cos(x) * FastMath.sin(x), f4.value(x), 1.0e-15);
+ Assert.assertEquals(FastMath.cos(x) * 5, f5.value(x), 1.0e-15);
+ Assert.assertEquals(FastMath.cos(x) / FastMath.sin(x), f6.value(x), 1.0e-15);
+ }
+
+ }
+
+ @Test
+ public void testCollector() throws Exception {
+
+ ComposableFunction f = BinaryFunction.POW.fix2ndArgument(2);
+ Assert.assertEquals(30, f.asCollector().value(new double[] { 1, 2, 3, 4 }), 1.0e-15);
+ Assert.assertEquals(33, f.asCollector(3).value(new double[] { 1, 2, 3, 4 }), 1.0e-15);
+ Assert.assertEquals(-30, f.asCollector(BinaryFunction.SUBTRACT).value(new double[] { 1, 2, 3, 4 }), 1.0e-15);
+ Assert.assertEquals(1152, f.asCollector(BinaryFunction.MULTIPLY, 2).value(new double[] { 1, 2, 3, 4 }), 1.0e-15);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/Expm1Function.java b/src/test/java/org/apache/commons/math/analysis/Expm1Function.java
new file mode 100644
index 0000000..b421f4d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/Expm1Function.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.util.FastMath;
+
+/**
+ * Auxiliary class for testing purposes.
+ *
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class Expm1Function implements DifferentiableUnivariateRealFunction {
+
+ public double value(double x) {
+ return FastMath.expm1(x);
+ }
+
+ public UnivariateRealFunction derivative() {
+ return new UnivariateRealFunction() {
+ public double value(double x) {
+ return FastMath.exp(x);
+ }
+ };
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/MonitoredFunction.java b/src/test/java/org/apache/commons/math/analysis/MonitoredFunction.java
new file mode 100644
index 0000000..44842b3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/MonitoredFunction.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.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+/**
+ * Wrapper class for counting functions calls.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public class MonitoredFunction implements UnivariateRealFunction {
+
+ public MonitoredFunction(UnivariateRealFunction f) {
+ callsCount = 0;
+ this.f = f;
+ }
+
+ public void setCallsCount(int callsCount) {
+ this.callsCount = callsCount;
+ }
+
+ public int getCallsCount() {
+ return callsCount;
+ }
+
+ public double value(double x) throws FunctionEvaluationException {
+ ++callsCount;
+ return f.value(x);
+ }
+
+ private int callsCount;
+ private UnivariateRealFunction f;
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/QuinticFunction.java b/src/test/java/org/apache/commons/math/analysis/QuinticFunction.java
new file mode 100644
index 0000000..0f7b177
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/QuinticFunction.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;
+
+/**
+ * Auxiliary class for testing solvers.
+ *
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class QuinticFunction implements DifferentiableUnivariateRealFunction {
+
+ /* Evaluate quintic.
+ * @see org.apache.commons.math.UnivariateRealFunction#value(double)
+ */
+ public double value(double x) {
+ return (x-1)*(x-0.5)*x*(x+0.5)*(x+1);
+ }
+
+ public UnivariateRealFunction derivative() {
+ return new UnivariateRealFunction() {
+ public double value(double x) {
+ return (5*x*x-3.75)*x*x+0.25;
+ }
+ };
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/SinFunction.java b/src/test/java/org/apache/commons/math/analysis/SinFunction.java
new file mode 100644
index 0000000..32bf95a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/SinFunction.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.analysis;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Auxillary class for testing solvers.
+ *
+ * The function is extraordinarily well behaved around zero roots: it
+ * has an inflection point there (second order derivative is zero),
+ * which means linear approximation (Regula Falsi) will converge
+ * quadratically.
+ *
+ * @version $Revision: 1037327 $ $Date: 2010-11-20 21:57:37 +0100 (sam. 20 nov. 2010) $
+ */
+public class SinFunction implements DifferentiableUnivariateRealFunction {
+
+ /* Evaluate sinus fuction.
+ * @see org.apache.commons.math.UnivariateRealFunction#value(double)
+ */
+ public double value(double x) {
+ return FastMath.sin(x);
+ }
+
+ /* First derivative of sinus function
+ */
+ public UnivariateRealFunction derivative() {
+ return new UnivariateRealFunction() {
+ public double value(double x) {
+ return FastMath.cos(x);
+ }
+ };
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/SincFunction.java b/src/test/java/org/apache/commons/math/analysis/SincFunction.java
new file mode 100644
index 0000000..d84ece0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/SincFunction.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.analysis;
+
+
+/**
+ * Auxiliary class for testing optimizers.
+ *
+ * @version $Revision$ $Date$
+ */
+public class SincFunction implements DifferentiableUnivariateRealFunction {
+ private static final double EPS = 1e-12;
+
+ /**
+ * @param x Argument.
+ * @return the value of this function at point {@code x}.
+ */
+ public double value(double x) {
+ return (Math.abs(x) < EPS ?
+ 1 :
+ Math.sin(x) / x);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public UnivariateRealFunction derivative() {
+ return new UnivariateRealFunction() {
+ public double value(double x) {
+ return (Math.abs(x) < EPS ?
+ 0 :
+ (x * Math.cos(x) - Math.sin(x)) / (x * x));
+ }
+ };
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/SumSincFunction.java b/src/test/java/org/apache/commons/math/analysis/SumSincFunction.java
new file mode 100644
index 0000000..707507c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/SumSincFunction.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.analysis;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+/**
+ * Auxiliary class for testing optimizers.
+ *
+ * @version $Revision$ $Date$
+ */
+public class SumSincFunction implements DifferentiableMultivariateRealFunction {
+ private static final DifferentiableUnivariateRealFunction sinc = new SincFunction();
+ private static final UnivariateRealFunction sincDeriv = sinc.derivative();
+
+ /**
+ * Factor that will multiply each term of the sum.
+ */
+ private final double factor;
+
+ /**
+ * @param factor Factor that will multiply each term of the sum.
+ */
+ public SumSincFunction(double factor) {
+ this.factor = factor;
+ }
+
+ /**
+ * @param point Argument.
+ * @return the value of this function at point {@code x}.
+ */
+ public double value(double[] point) throws FunctionEvaluationException {
+ double sum = 0;
+ for (int i = 0, max = point.length; i < max; i++) {
+ final double x = point[i];
+ final double v = sinc.value(x);
+ sum += v;
+ }
+ return factor * sum;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MultivariateRealFunction partialDerivative(final int k) {
+ return new MultivariateRealFunction() {
+ public double value(double[] point) throws FunctionEvaluationException {
+ return sincDeriv.value(point[k]);
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MultivariateVectorialFunction gradient() {
+ return new MultivariateVectorialFunction() {
+ public double[] value(double[] point)
+ throws FunctionEvaluationException {
+ final int n = point.length;
+ final double[] r = new double[n];
+ for (int i = 0; i < n; i++) {
+ final double x = point[i];
+ r[i] = factor * sincDeriv.value(x);
+ }
+ return r;
+ }
+ };
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegratorTest.java b/src/test/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegratorTest.java
new file mode 100644
index 0000000..3145506
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/integration/LegendreGaussIntegratorTest.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.analysis.integration;
+
+import java.util.Random;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class LegendreGaussIntegratorTest
+extends TestCase {
+
+ public LegendreGaussIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new LegendreGaussIntegrator(5, 64);
+ integrator.setAbsoluteAccuracy(1.0e-10);
+ integrator.setRelativeAccuracy(1.0e-14);
+ integrator.setMinimalIterationCount(2);
+ integrator.setMaximalIterationCount(15);
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = FastMath.PI; expected = 2;
+ tolerance = FastMath.max(integrator.getAbsoluteAccuracy(),
+ FastMath.abs(expected * integrator.getRelativeAccuracy()));
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -FastMath.PI/3; max = 0; expected = -0.5;
+ tolerance = FastMath.max(integrator.getAbsoluteAccuracy(),
+ FastMath.abs(expected * integrator.getRelativeAccuracy()));
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealIntegrator integrator = new LegendreGaussIntegrator(3, 64);
+ double min, max, expected, result;
+
+ min = 0; max = 1; expected = -1.0/48;
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, 1.0e-16);
+
+ min = 0; max = 0.5; expected = 11.0/768;
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, 1.0e-16);
+
+ min = -1; max = 4; expected = 2048/3.0 - 78 + 1.0/48;
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, 1.0e-16);
+ }
+
+ public void testExactIntegration()
+ throws ConvergenceException, FunctionEvaluationException {
+ Random random = new Random(86343623467878363l);
+ for (int n = 2; n < 6; ++n) {
+ LegendreGaussIntegrator integrator =
+ new LegendreGaussIntegrator(n, 64);
+
+ // an n points Gauss-Legendre integrator integrates 2n-1 degree polynoms exactly
+ for (int degree = 0; degree <= 2 * n - 1; ++degree) {
+ for (int i = 0; i < 10; ++i) {
+ double[] coeff = new double[degree + 1];
+ for (int k = 0; k < coeff.length; ++k) {
+ coeff[k] = 2 * random.nextDouble() - 1;
+ }
+ PolynomialFunction p = new PolynomialFunction(coeff);
+ double result = integrator.integrate(p, -5.0, 15.0);
+ double reference = exactIntegration(p, -5.0, 15.0);
+ assertEquals(n + " " + degree + " " + i, reference, result, 1.0e-12 * (1.0 + FastMath.abs(reference)));
+ }
+ }
+
+ }
+ }
+
+ private double exactIntegration(PolynomialFunction p, double a, double b) {
+ final double[] coeffs = p.getCoefficients();
+ double yb = coeffs[coeffs.length - 1] / coeffs.length;
+ double ya = yb;
+ for (int i = coeffs.length - 2; i >= 0; --i) {
+ yb = yb * b + coeffs[i] / (i + 1);
+ ya = ya * a + coeffs[i] / (i + 1);
+ }
+ return yb * b - ya * a;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/integration/RombergIntegratorTest.java b/src/test/java/org/apache/commons/math/analysis/integration/RombergIntegratorTest.java
new file mode 100644
index 0000000..1174107
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/integration/RombergIntegratorTest.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.analysis.integration;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Romberg integrator.
+ * <p>
+ * Romberg algorithm is very fast for good behavior integrand. Test runs
+ * show that for a default relative accuracy of 1E-6, it generally takes
+ * takes less than 5 iterations for the integral to converge.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class RombergIntegratorTest extends TestCase {
+
+ /**
+ * Test of integrator for the sine function.
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new RombergIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = FastMath.PI; expected = 2;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -FastMath.PI/3; max = 0; expected = -0.5;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of integrator for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealIntegrator integrator = new RombergIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = 1; expected = -1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0; max = 0.5; expected = 11.0/768;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1; max = 4; expected = 2048/3.0 - 78 + 1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the integrator.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new RombergIntegrator();
+
+ try {
+ // bad interval
+ integrator.integrate(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(5);
+ integrator.setMaximalIterationCount(4);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(10);
+ integrator.setMaximalIterationCount(50);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/integration/SimpsonIntegratorTest.java b/src/test/java/org/apache/commons/math/analysis/integration/SimpsonIntegratorTest.java
new file mode 100644
index 0000000..a60915a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/integration/SimpsonIntegratorTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.integration;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Simpson integrator.
+ * <p>
+ * Test runs show that for a default relative accuracy of 1E-6, it
+ * generally takes 5 to 10 iterations for the integral to converge.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class SimpsonIntegratorTest extends TestCase {
+
+ /**
+ * Test of integrator for the sine function.
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new SimpsonIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = FastMath.PI; expected = 2;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -FastMath.PI/3; max = 0; expected = -0.5;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of integrator for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealIntegrator integrator = new SimpsonIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = 1; expected = -1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0; max = 0.5; expected = 11.0/768;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1; max = 4; expected = 2048/3.0 - 78 + 1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the integrator.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new SimpsonIntegrator();
+
+ try {
+ // bad interval
+ integrator.integrate(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(5);
+ integrator.setMaximalIterationCount(4);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(10);
+ integrator.setMaximalIterationCount(99);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/integration/TrapezoidIntegratorTest.java b/src/test/java/org/apache/commons/math/analysis/integration/TrapezoidIntegratorTest.java
new file mode 100644
index 0000000..dbecce1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/integration/TrapezoidIntegratorTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.integration;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for trapezoid integrator.
+ * <p>
+ * Test runs show that for a default relative accuracy of 1E-6, it
+ * generally takes 10 to 15 iterations for the integral to converge.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class TrapezoidIntegratorTest extends TestCase {
+
+ /**
+ * Test of integrator for the sine function.
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new TrapezoidIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = FastMath.PI; expected = 2;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -FastMath.PI/3; max = 0; expected = -0.5;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of integrator for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealIntegrator integrator = new TrapezoidIntegrator();
+ double min, max, expected, result, tolerance;
+
+ min = 0; max = 1; expected = -1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0; max = 0.5; expected = 11.0/768;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1; max = 4; expected = 2048/3.0 - 78 + 1.0/48;
+ tolerance = FastMath.abs(expected * integrator.getRelativeAccuracy());
+ result = integrator.integrate(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the integrator.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealIntegrator integrator = new TrapezoidIntegrator();
+
+ try {
+ // bad interval
+ integrator.integrate(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(5);
+ integrator.setMaximalIterationCount(4);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad iteration limits
+ integrator.setMinimalIterationCount(10);
+ integrator.setMaximalIterationCount(99);
+ integrator.integrate(f, -1, 1);
+ fail("Expecting IllegalArgumentException - bad iteration limits");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
new file mode 100644
index 0000000..32ca91b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
@@ -0,0 +1,448 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FunctionEvaluationException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the bicubic function.
+ *
+ * @version $Revision: 821626 $ $Date: 2009-10-04 23:57:30 +0200 (Sun, 04 Oct 2009) $
+ */
+public final class BicubicSplineInterpolatingFunctionTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+
+ @SuppressWarnings("unused")
+ BivariateRealFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+ zval, zval, zval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(wxval, yval, zval, zval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ double[] wyval = new double[] {-4, -1, -1, 2.5};
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, wyval, zval, zval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ double[][] wzval = new double[xval.length][yval.length - 1];
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+
+ wzval = new double[xval.length - 1][yval.length];
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ Assert.assertNotNull(bcf); // Avoid Findbugs "dead store" warning
+ }
+
+ /**
+ * Test for a plane.
+ * <p>
+ * z = 2 x - 3 y + 5
+ */
+ @Test
+ public void testPlane() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ // Function values
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x - 3 * y + 5;
+ }
+ };
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+ // Partial derivatives with respect to x
+ double[][] dZdX = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdX[i][j] = 2;
+ }
+ }
+ // Partial derivatives with respect to y
+ double[][] dZdY = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdY[i][j] = -3;
+ }
+ }
+ // Partial cross-derivatives
+ double[][] dZdXdY = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdXdY[i][j] = 0;
+ }
+ }
+
+ BivariateRealFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+ dZdX, dZdY, dZdXdY);
+ double x, y;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("On sample point",
+ expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("Half-way between sample points (middle of the patch)",
+ expected, result, 0.3);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("Half-way between sample points (border of the patch)",
+ expected, result, 0.3);
+ }
+
+ /**
+ * Test for a paraboloid.
+ * <p>
+ * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+ */
+ @Test
+ public void testParaboloid() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ // Function values
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+ }
+ };
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+ // Partial derivatives with respect to x
+ double[][] dZdX = new double[xval.length][yval.length];
+ BivariateRealFunction dfdX = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 4 * (x + y);
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdX[i][j] = dfdX.value(xval[i], yval[j]);
+ }
+ }
+ // Partial derivatives with respect to y
+ double[][] dZdY = new double[xval.length][yval.length];
+ BivariateRealFunction dfdY = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 4 * x - 6 * y;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdY[i][j] = dfdY.value(xval[i], yval[j]);
+ }
+ }
+ // Partial cross-derivatives
+ double[][] dZdXdY = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ dZdXdY[i][j] = 4;
+ }
+ }
+
+ BivariateRealFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+ dZdX, dZdY, dZdXdY);
+ double x, y;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("On sample point",
+ expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("Half-way between sample points (middle of the patch)",
+ expected, result, 2);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = bcf.value(x, y);
+ Assert.assertEquals("Half-way between sample points (border of the patch)",
+ expected, result, 2);
+ }
+
+ /**
+ * Test for partial derivatives of {@link BicubicSplineFunction}.
+ * <p>
+ * f(x, y) = Σ<sub>i</sub>Σ<sub>j</sub> (i+1) (j+2) x<sup>i</sup> y<sup>j</sup>
+ */
+ @Test
+ public void testSplinePartialDerivatives() throws FunctionEvaluationException {
+ final int N = 4;
+ final double[] coeff = new double[16];
+
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ coeff[i + N * j] = (i + 1) * (j + 2);
+ }
+ }
+
+ final BicubicSplineFunction f = new BicubicSplineFunction(coeff);
+ BivariateRealFunction derivative;
+ final double x = 0.435;
+ final double y = 0.776;
+ final double tol = 1e-13;
+
+ derivative = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double yFactor = 2 + 3 * y + 4 * y2 + 5 * y3;
+ return yFactor * (2 + 6 * x + 12 * x2);
+ }
+ };
+ Assert.assertEquals("dFdX", derivative.value(x, y),
+ f.partialDerivativeX().value(x, y), tol);
+
+ derivative = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double y2 = y * y;
+ final double xFactor = 1 + 2 * x + 3 * x2 + 4 * x3;
+ return xFactor * (3 + 8 * y + 15 * y2);
+ }
+ };
+ Assert.assertEquals("dFdY", derivative.value(x, y),
+ f.partialDerivativeY().value(x, y), tol);
+
+ derivative = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double yFactor = 2 + 3 * y + 4 * y2 + 5 * y3;
+ return yFactor * (6 + 24 * x);
+ }
+ };
+ Assert.assertEquals("d2FdX2", derivative.value(x, y),
+ f.partialDerivativeXX().value(x, y), tol);
+
+ derivative = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double xFactor = 1 + 2 * x + 3 * x2 + 4 * x3;
+ return xFactor * (8 + 30 * y);
+ }
+ };
+ Assert.assertEquals("d2FdY2", derivative.value(x, y),
+ f.partialDerivativeYY().value(x, y), tol);
+
+ derivative = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double y2 = y * y;
+ final double yFactor = 3 + 8 * y + 15 * y2;
+ return yFactor * (2 + 6 * x + 12 * x2);
+ }
+ };
+ Assert.assertEquals("d2FdXdY", derivative.value(x, y),
+ f.partialDerivativeXY().value(x, y), tol);
+ }
+
+ /**
+ * Test that the partial derivatives computed from a
+ * {@link BicubicSplineInterpolatingFunction} match the input data.
+ * <p>
+ * f(x, y) = 5
+ * - 3 x + 2 y
+ * - x y + 2 x<sup>2</sup> - 3 y<sup>2</sup>
+ * + 4 x<sup>2</sup> y - x y<sup>2</sup> - 3 x<sup>3</sup> + y<sup>3</sup>
+ */
+ @Test
+ public void testMatchingPartialDerivatives() throws MathException {
+ final int sz = 21;
+ double[] val = new double[sz];
+ // Coordinate values
+ final double delta = 1d / (sz - 1);
+ for (int i = 0; i < sz; i++) {
+ val[i] = i * delta;
+ }
+ // Function values
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+
+ return 5
+ - 3 * x + 2 * y
+ - x * y + 2 * x2 - 3 * y2
+ + 4 * x2 * y - x * y2 - 3 * x3 + y3;
+ }
+ };
+ double[][] fval = new double[sz][sz];
+ for (int i = 0; i < sz; i++) {
+ for (int j = 0; j < sz; j++) {
+ fval[i][j] = f.value(val[i], val[j]);
+ }
+ }
+ // Partial derivatives with respect to x
+ double[][] dFdX = new double[sz][sz];
+ BivariateRealFunction dfdX = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double y2 = y * y;
+ return - 3 - y + 4 * x + 8 * x * y - y2 - 9 * x2;
+ }
+ };
+ for (int i = 0; i < sz; i++) {
+ for (int j = 0; j < sz; j++) {
+ dFdX[i][j] = dfdX.value(val[i], val[j]);
+ }
+ }
+ // Partial derivatives with respect to y
+ double[][] dFdY = new double[sz][sz];
+ BivariateRealFunction dfdY = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double y2 = y * y;
+ return 2 - x - 6 * y + 4 * x2 - 2 * x * y + 3 * y2;
+ }
+ };
+ for (int i = 0; i < sz; i++) {
+ for (int j = 0; j < sz; j++) {
+ dFdY[i][j] = dfdY.value(val[i], val[j]);
+ }
+ }
+ // Partial cross-derivatives
+ double[][] d2FdXdY = new double[sz][sz];
+ BivariateRealFunction d2fdXdY = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return -1 + 8 * x - 2 * y;
+ }
+ };
+ for (int i = 0; i < sz; i++) {
+ for (int j = 0; j < sz; j++) {
+ d2FdXdY[i][j] = d2fdXdY.value(val[i], val[j]);
+ }
+ }
+
+ BicubicSplineInterpolatingFunction bcf
+ = new BicubicSplineInterpolatingFunction(val, val, fval, dFdX, dFdY, d2FdXdY);
+
+ double x, y;
+ double expected, result;
+
+ final double tol = 1e-12;
+ for (int i = 0; i < sz; i++) {
+ x = val[i];
+ for (int j = 0; j < sz; j++) {
+ y = val[j];
+
+ expected = dfdX.value(x, y);
+ result = bcf.partialDerivativeX(x, y);
+ Assert.assertEquals(x + " " + y + " dFdX", expected, result, tol);
+
+ expected = dfdY.value(x, y);
+ result = bcf.partialDerivativeY(x, y);
+ Assert.assertEquals(x + " " + y + " dFdY", expected, result, tol);
+
+ expected = d2fdXdY.value(x, y);
+ result = bcf.partialDerivativeXY(x, y);
+ Assert.assertEquals(x + " " + y + " d2FdXdY", expected, result, tol);
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatorTest.java
new file mode 100644
index 0000000..de807a4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/BicubicSplineInterpolatorTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the bicubic interpolator.
+ *
+ * @version $Revision: 821626 $ $Date: 2009-10-04 23:57:30 +0200 (Sun, 04 Oct 2009) $
+ */
+public final class BicubicSplineInterpolatorTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+
+ BivariateRealGridInterpolator interpolator = new BicubicSplineInterpolator();
+
+ @SuppressWarnings("unused")
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ p = interpolator.interpolate(wxval, yval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[] wyval = new double[] {-4, -3, -1, -1};
+ try {
+ p = interpolator.interpolate(xval, wyval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[][] wzval = new double[xval.length][yval.length + 1];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wzval = new double[xval.length - 1][yval.length];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ }
+
+ /**
+ * Test of interpolator for a plane.
+ * <p>
+ * z = 2 x - 3 y + 5
+ */
+ @Test
+ public void testPlane() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x - 3 * y + 5;
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new BicubicSplineInterpolator();
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 0.3);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 0.3);
+ }
+
+ /**
+ * Test of interpolator for a paraboloid.
+ * <p>
+ * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+ */
+ @Test
+ public void testParaboloid() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new BicubicSplineInterpolator();
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -2, -1, 0.5, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 5;
+ y = 0.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 1e-13);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 0.2);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 0.2);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolatorTest.java
new file mode 100644
index 0000000..3054cfb
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/DividedDifferenceInterpolatorTest.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.MathException;
+import org.apache.commons.math.analysis.Expm1Function;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Divided Difference interpolator.
+ * <p>
+ * The error of polynomial interpolation is
+ * f(z) - p(z) = f^(n)(zeta) * (z-x[0])(z-x[1])...(z-x[n-1]) / n!
+ * where f^(n) is the n-th derivative of the approximated function and
+ * zeta is some point in the interval determined by x[] and z.
+ * <p>
+ * Since zeta is unknown, f^(n)(zeta) cannot be calculated. But we can bound
+ * it and use the absolute value upper bound for estimates. For reference,
+ * see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, chapter 2.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class DividedDifferenceInterpolatorTest extends TestCase {
+
+ /**
+ * Test of interpolator for the sine function.
+ * <p>
+ * |sin^(n)(zeta)| <= 1.0, zeta in [0, 2*PI]
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealInterpolator interpolator = new DividedDifferenceInterpolator();
+ double x[], y[], z, expected, result, tolerance;
+
+ // 6 interpolating points on interval [0, 2*PI]
+ int n = 6;
+ double min = 0.0, max = 2 * FastMath.PI;
+ x = new double[n];
+ y = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = min + i * (max - min) / n;
+ y[i] = f.value(x[i]);
+ }
+ double derivativebound = 1.0;
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+
+ z = FastMath.PI / 4; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = FastMath.PI * 1.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of interpolator for the exponential function.
+ * <p>
+ * |expm1^(n)(zeta)| <= e, zeta in [-1, 1]
+ */
+ public void testExpm1Function() throws MathException {
+ UnivariateRealFunction f = new Expm1Function();
+ UnivariateRealInterpolator interpolator = new DividedDifferenceInterpolator();
+ double x[], y[], z, expected, result, tolerance;
+
+ // 5 interpolating points on interval [-1, 1]
+ int n = 5;
+ double min = -1.0, max = 1.0;
+ x = new double[n];
+ y = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = min + i * (max - min) / n;
+ y[i] = f.value(x[i]);
+ }
+ double derivativebound = FastMath.E;
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+
+ z = 0.0; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = 0.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = -0.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the interpolator.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealInterpolator interpolator = new DividedDifferenceInterpolator();
+
+ try {
+ // bad abscissas array
+ double x[] = { 1.0, 2.0, 2.0, 4.0 };
+ double y[] = { 0.0, 4.0, 4.0, 2.5 };
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+ p.value(0.0);
+ fail("Expecting MathException - bad abscissas array");
+ } catch (MathException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Returns the partial error term (z-x[0])(z-x[1])...(z-x[n-1])/n!
+ */
+ protected double partialerror(double x[], double z) throws
+ IllegalArgumentException {
+
+ if (x.length < 1) {
+ throw new IllegalArgumentException
+ ("Interpolation array cannot be empty.");
+ }
+ double out = 1;
+ for (int i = 0; i < x.length; i++) {
+ out *= (z - x[i]) / (i + 1);
+ }
+ return out;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/LinearInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/LinearInterpolatorTest.java
new file mode 100644
index 0000000..19c96e3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/LinearInterpolatorTest.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.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.NonMonotonousSequenceException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test the LinearInterpolator.
+ */
+public class LinearInterpolatorTest {
+
+ /** error tolerance for spline interpolator value at knot points */
+ protected double knotTolerance = 1E-12;
+
+ /** error tolerance for interpolating polynomial coefficients */
+ protected double coefficientTolerance = 1E-6;
+
+ /** error tolerance for interpolated values */
+ protected double interpolationTolerance = 1E-12;
+
+ @Test
+ public void testInterpolateLinearDegenerateTwoSegment()
+ throws Exception {
+ double x[] = { 0.0, 0.5, 1.0 };
+ double y[] = { 0.0, 0.5, 1.0 };
+ UnivariateRealInterpolator i = new LinearInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 1d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+
+ // Check interpolation
+ Assert.assertEquals(0.0,f.value(0.0), interpolationTolerance);
+ Assert.assertEquals(0.4,f.value(0.4), interpolationTolerance);
+ Assert.assertEquals(1.0,f.value(1.0), interpolationTolerance);
+ }
+
+ @Test
+ public void testInterpolateLinearDegenerateThreeSegment()
+ throws Exception {
+ double x[] = { 0.0, 0.5, 1.0, 1.5 };
+ double y[] = { 0.0, 0.5, 1.0, 1.5 };
+ UnivariateRealInterpolator i = new LinearInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 1d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[2], 1d};
+ TestUtils.assertEquals(polynomials[2].getCoefficients(), target, coefficientTolerance);
+
+ // Check interpolation
+ Assert.assertEquals(0,f.value(0), interpolationTolerance);
+ Assert.assertEquals(1.4,f.value(1.4), interpolationTolerance);
+ Assert.assertEquals(1.5,f.value(1.5), interpolationTolerance);
+ }
+
+ @Test
+ public void testInterpolateLinear() throws Exception {
+ double x[] = { 0.0, 0.5, 1.0 };
+ double y[] = { 0.0, 0.5, 0.0 };
+ UnivariateRealInterpolator i = new LinearInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], -1d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+ }
+
+ @Test
+ public void testIllegalArguments() throws MathException {
+ // Data set arrays of different size.
+ UnivariateRealInterpolator i = new LinearInterpolator();
+ try {
+ double xval[] = { 0.0, 1.0 };
+ double yval[] = { 0.0, 1.0, 2.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect data set array with different sizes.");
+ } catch (DimensionMismatchException iae) {
+ // Expected.
+ }
+ // X values not sorted.
+ try {
+ double xval[] = { 0.0, 1.0, 0.5 };
+ double yval[] = { 0.0, 1.0, 2.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect unsorted arguments.");
+ } catch (NonMonotonousSequenceException iae) {
+ // Expected.
+ }
+ // Not enough data to interpolate.
+ try {
+ double xval[] = { 0.0 };
+ double yval[] = { 0.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect unsorted arguments.");
+ } catch (NumberIsTooSmallException iae) {
+ // Expected.
+ }
+ }
+
+ /**
+ * verifies that f(x[i]) = y[i] for i = 0..n-1 where n is common length.
+ */
+ protected void verifyInterpolation(UnivariateRealFunction f, double x[], double y[])
+ throws Exception{
+ for (int i = 0; i < x.length; i++) {
+ Assert.assertEquals(f.value(x[i]), y[i], knotTolerance);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/LoessInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/LoessInterpolatorTest.java
new file mode 100644
index 0000000..1fea733
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/LoessInterpolatorTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test of the LoessInterpolator class.
+ */
+public class LoessInterpolatorTest {
+
+ @Test
+ public void testOnOnePoint() throws MathException {
+ double[] xval = {0.5};
+ double[] yval = {0.7};
+ double[] res = new LoessInterpolator().smooth(xval, yval);
+ Assert.assertEquals(1, res.length);
+ Assert.assertEquals(0.7, res[0], 0.0);
+ }
+
+ @Test
+ public void testOnTwoPoints() throws MathException {
+ double[] xval = {0.5, 0.6};
+ double[] yval = {0.7, 0.8};
+ double[] res = new LoessInterpolator().smooth(xval, yval);
+ Assert.assertEquals(2, res.length);
+ Assert.assertEquals(0.7, res[0], 0.0);
+ Assert.assertEquals(0.8, res[1], 0.0);
+ }
+
+ @Test
+ public void testOnStraightLine() throws MathException {
+ double[] xval = {1,2,3,4,5};
+ double[] yval = {2,4,6,8,10};
+ LoessInterpolator li = new LoessInterpolator(0.6, 2, 1e-12);
+ double[] res = li.smooth(xval, yval);
+ Assert.assertEquals(5, res.length);
+ for(int i = 0; i < 5; ++i) {
+ Assert.assertEquals(yval[i], res[i], 1e-8);
+ }
+ }
+
+ @Test
+ public void testOnDistortedSine() throws MathException {
+ int numPoints = 100;
+ double[] xval = new double[numPoints];
+ double[] yval = new double[numPoints];
+ double xnoise = 0.1;
+ double ynoise = 0.2;
+
+ generateSineData(xval, yval, xnoise, ynoise);
+
+ LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
+
+ double[] res = li.smooth(xval, yval);
+
+ // Check that the resulting curve differs from
+ // the "real" sine less than the jittered one
+
+ double noisyResidualSum = 0;
+ double fitResidualSum = 0;
+
+ for(int i = 0; i < numPoints; ++i) {
+ double expected = FastMath.sin(xval[i]);
+ double noisy = yval[i];
+ double fit = res[i];
+
+ noisyResidualSum += FastMath.pow(noisy - expected, 2);
+ fitResidualSum += FastMath.pow(fit - expected, 2);
+ }
+
+ Assert.assertTrue(fitResidualSum < noisyResidualSum);
+ }
+
+ @Test
+ public void testIncreasingBandwidthIncreasesSmoothness() throws MathException {
+ int numPoints = 100;
+ double[] xval = new double[numPoints];
+ double[] yval = new double[numPoints];
+ double xnoise = 0.1;
+ double ynoise = 0.1;
+
+ generateSineData(xval, yval, xnoise, ynoise);
+
+ // Check that variance decreases as bandwidth increases
+
+ double[] bandwidths = {0.1, 0.5, 1.0};
+ double[] variances = new double[bandwidths.length];
+ for (int i = 0; i < bandwidths.length; i++) {
+ double bw = bandwidths[i];
+
+ LoessInterpolator li = new LoessInterpolator(bw, 4, 1e-12);
+
+ double[] res = li.smooth(xval, yval);
+
+ for (int j = 1; j < res.length; ++j) {
+ variances[i] += FastMath.pow(res[j] - res[j-1], 2);
+ }
+ }
+
+ for(int i = 1; i < variances.length; ++i) {
+ Assert.assertTrue(variances[i] < variances[i-1]);
+ }
+ }
+
+ @Test
+ public void testIncreasingRobustnessItersIncreasesSmoothnessWithOutliers() throws MathException {
+ int numPoints = 100;
+ double[] xval = new double[numPoints];
+ double[] yval = new double[numPoints];
+ double xnoise = 0.1;
+ double ynoise = 0.1;
+
+ generateSineData(xval, yval, xnoise, ynoise);
+
+ // Introduce a couple of outliers
+ yval[numPoints/3] *= 100;
+ yval[2 * numPoints/3] *= -100;
+
+ // Check that variance decreases as the number of robustness
+ // iterations increases
+
+ double[] variances = new double[4];
+ for (int i = 0; i < 4; i++) {
+ LoessInterpolator li = new LoessInterpolator(0.3, i, 1e-12);
+
+ double[] res = li.smooth(xval, yval);
+
+ for (int j = 1; j < res.length; ++j) {
+ variances[i] += FastMath.abs(res[j] - res[j-1]);
+ }
+ }
+
+ for(int i = 1; i < variances.length; ++i) {
+ Assert.assertTrue(variances[i] < variances[i-1]);
+ }
+ }
+
+ @Test(expected=MathException.class)
+ public void testUnequalSizeArguments() throws MathException {
+ new LoessInterpolator().smooth(new double[] {1,2,3}, new double[] {1,2,3,4});
+ }
+
+ @Test(expected=MathException.class)
+ public void testEmptyData() throws MathException {
+ new LoessInterpolator().smooth(new double[] {}, new double[] {});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNonStrictlyIncreasing1() throws MathException {
+ new LoessInterpolator().smooth(new double[] {4,3,1,2}, new double[] {3,4,5,6});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNonStrictlyIncreasing2() throws MathException {
+ new LoessInterpolator().smooth(new double[] {1,2,2,3}, new double[] {3,4,5,6});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal1() throws MathException {
+ new LoessInterpolator().smooth(new double[] {1,2,Double.NaN}, new double[] {3,4,5});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal2() throws MathException {
+ new LoessInterpolator().smooth(new double[] {1,2,Double.POSITIVE_INFINITY}, new double[] {3,4,5});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal3() throws MathException {
+ new LoessInterpolator().smooth(new double[] {1,2,Double.NEGATIVE_INFINITY}, new double[] {3,4,5});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal4() throws MathException {
+ new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NaN});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal5() throws MathException {
+ new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.POSITIVE_INFINITY});
+ }
+
+ @Test(expected=MathException.class)
+ public void testNotAllFiniteReal6() throws MathException {
+ new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NEGATIVE_INFINITY});
+ }
+
+ @Test(expected=MathException.class)
+ public void testInsufficientBandwidth() throws MathException {
+ LoessInterpolator li = new LoessInterpolator(0.1, 3, 1e-12);
+ li.smooth(new double[] {1,2,3,4,5,6,7,8,9,10,11,12}, new double[] {1,2,3,4,5,6,7,8,9,10,11,12});
+ }
+
+ @Test(expected=MathException.class)
+ public void testCompletelyIncorrectBandwidth1() throws MathException {
+ new LoessInterpolator(-0.2, 3, 1e-12);
+ }
+
+ @Test(expected=MathException.class)
+ public void testCompletelyIncorrectBandwidth2() throws MathException {
+ new LoessInterpolator(1.1, 3, 1e-12);
+ }
+
+ @Test
+ public void testMath296withoutWeights() throws MathException {
+ double[] xval = {
+ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
+ 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0};
+ double[] yval = {
+ 0.47, 0.48, 0.55, 0.56, -0.08, -0.04, -0.07, -0.07,
+ -0.56, -0.46, -0.56, -0.52, -3.03, -3.08, -3.09,
+ -3.04, 3.54, 3.46, 3.36, 3.35};
+ // Output from R, rounded to .001
+ double[] yref = {
+ 0.461, 0.499, 0.541, 0.308, 0.175, -0.042, -0.072,
+ -0.196, -0.311, -0.446, -0.557, -1.497, -2.133,
+ -3.08, -3.09, -0.621, 0.982, 3.449, 3.389, 3.336
+ };
+ LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
+ double[] res = li.smooth(xval, yval);
+ Assert.assertEquals(xval.length, res.length);
+ for(int i = 0; i < res.length; ++i) {
+ Assert.assertEquals(yref[i], res[i], 0.02);
+ }
+ }
+
+ private void generateSineData(double[] xval, double[] yval, double xnoise, double ynoise) {
+ double dx = 2 * FastMath.PI / xval.length;
+ double x = 0;
+ for(int i = 0; i < xval.length; ++i) {
+ xval[i] = x;
+ yval[i] = FastMath.sin(x) + (2 * FastMath.random() - 1) * ynoise;
+ x += dx * (1 + (2 * FastMath.random() - 1) * xnoise);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatorTest.java
new file mode 100644
index 0000000..742d59d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/MicrosphereInterpolatorTest.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.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the "microsphere projection" interpolator.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class MicrosphereInterpolatorTest {
+ /**
+ * Test of interpolator for a plane.
+ * <p>
+ * y = 2 x<sub>1</sub> - 3 x<sub>2</sub> + 5
+ */
+ @Test
+ public void testLinearFunction2D() throws MathException {
+ MultivariateRealFunction f = new MultivariateRealFunction() {
+ public double value(double[] x) {
+ if (x.length != 2) {
+ throw new IllegalArgumentException();
+ }
+ return 2 * x[0] - 3 * x[1] + 5;
+ }
+ };
+
+ MultivariateRealInterpolator interpolator = new MicrosphereInterpolator();
+
+ // Interpolating points in [-1, 1][-1, 1] by steps of 1.
+ final int n = 9;
+ final int dim = 2;
+ double[][] x = new double[n][dim];
+ double[] y = new double[n];
+ int index = 0;
+ for (int i = -1; i <= 1; i++) {
+ for (int j = -1; j <= 1; j++) {
+ x[index][0] = i;
+ x[index][1] = j;
+ y[index] = f.value(x[index]);
+ ++index;
+ }
+ }
+
+ MultivariateRealFunction p = interpolator.interpolate(x, y);
+
+ double[] c = new double[dim];
+ double expected, result;
+
+ c[0] = 0;
+ c[1] = 0;
+ expected = f.value(c);
+ result = p.value(c);
+ Assert.assertEquals("On sample point", expected, result, FastMath.ulp(1d));
+
+ c[0] = 0 + 1e-5;
+ c[1] = 1 - 1e-5;
+ expected = f.value(c);
+ result = p.value(c);
+ Assert.assertEquals("1e-5 away from sample point", expected, result, 1e-4);
+ }
+
+ /**
+ * Test of interpolator for a quadratic function.
+ * <p>
+ * y = 2 x<sub>1</sub><sup>2</sup> - 3 x<sub>2</sub><sup>2</sup>
+ * + 4 x<sub>1</sub> x<sub>2</sub> - 5
+ */
+ @Test
+ public void testParaboloid2D() throws MathException {
+ MultivariateRealFunction f = new MultivariateRealFunction() {
+ public double value(double[] x) {
+ if (x.length != 2) {
+ throw new IllegalArgumentException();
+ }
+ return 2 * x[0] * x[0] - 3 * x[1] * x[1] + 4 * x[0] * x[1] - 5;
+ }
+ };
+
+ MultivariateRealInterpolator interpolator = new MicrosphereInterpolator();
+
+ // Interpolating points in [-10, 10][-10, 10] by steps of 2.
+ final int n = 121;
+ final int dim = 2;
+ double[][] x = new double[n][dim];
+ double[] y = new double[n];
+ int index = 0;
+ for (int i = -10; i <= 10; i += 2) {
+ for (int j = -10; j <= 10; j += 2) {
+ x[index][0] = i;
+ x[index][1] = j;
+ y[index] = f.value(x[index]);
+ ++index;
+ }
+ }
+
+ MultivariateRealFunction p = interpolator.interpolate(x, y);
+
+ double[] c = new double[dim];
+ double expected, result;
+
+ c[0] = 0;
+ c[1] = 0;
+ expected = f.value(c);
+ result = p.value(c);
+ Assert.assertEquals("On sample point", expected, result, FastMath.ulp(1d));
+
+ c[0] = 2 + 1e-5;
+ c[1] = 2 - 1e-5;
+ expected = f.value(c);
+ result = p.value(c);
+ Assert.assertEquals("1e-5 away from sample point", expected, result, 1e-3);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolatorTest.java
new file mode 100644
index 0000000..72e87ac
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/NevilleInterpolatorTest.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.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.Expm1Function;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Neville interpolator.
+ * <p>
+ * The error of polynomial interpolation is
+ * f(z) - p(z) = f^(n)(zeta) * (z-x[0])(z-x[1])...(z-x[n-1]) / n!
+ * where f^(n) is the n-th derivative of the approximated function and
+ * zeta is some point in the interval determined by x[] and z.
+ * <p>
+ * Since zeta is unknown, f^(n)(zeta) cannot be calculated. But we can bound
+ * it and use the absolute value upper bound for estimates. For reference,
+ * see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X, chapter 2.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ */
+public final class NevilleInterpolatorTest extends TestCase {
+
+ /**
+ * Test of interpolator for the sine function.
+ * <p>
+ * |sin^(n)(zeta)| <= 1.0, zeta in [0, 2*PI]
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealInterpolator interpolator = new NevilleInterpolator();
+ double x[], y[], z, expected, result, tolerance;
+
+ // 6 interpolating points on interval [0, 2*PI]
+ int n = 6;
+ double min = 0.0, max = 2 * FastMath.PI;
+ x = new double[n];
+ y = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = min + i * (max - min) / n;
+ y[i] = f.value(x[i]);
+ }
+ double derivativebound = 1.0;
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+
+ z = FastMath.PI / 4; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = FastMath.PI * 1.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of interpolator for the exponential function.
+ * <p>
+ * |expm1^(n)(zeta)| <= e, zeta in [-1, 1]
+ */
+ public void testExpm1Function() throws MathException {
+ UnivariateRealFunction f = new Expm1Function();
+ UnivariateRealInterpolator interpolator = new NevilleInterpolator();
+ double x[], y[], z, expected, result, tolerance;
+
+ // 5 interpolating points on interval [-1, 1]
+ int n = 5;
+ double min = -1.0, max = 1.0;
+ x = new double[n];
+ y = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = min + i * (max - min) / n;
+ y[i] = f.value(x[i]);
+ }
+ double derivativebound = FastMath.E;
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+
+ z = 0.0; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = 0.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+
+ z = -0.5; expected = f.value(z); result = p.value(z);
+ tolerance = FastMath.abs(derivativebound * partialerror(x, z));
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the interpolator.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealInterpolator interpolator = new NevilleInterpolator();
+
+ try {
+ // bad abscissas array
+ double x[] = { 1.0, 2.0, 2.0, 4.0 };
+ double y[] = { 0.0, 4.0, 4.0, 2.5 };
+ UnivariateRealFunction p = interpolator.interpolate(x, y);
+ p.value(0.0);
+ fail("Expecting FunctionEvaluationException - bad abscissas array");
+ } catch (FunctionEvaluationException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Returns the partial error term (z-x[0])(z-x[1])...(z-x[n-1])/n!
+ */
+ protected double partialerror(double x[], double z) throws
+ IllegalArgumentException {
+
+ if (x.length < 1) {
+ throw new IllegalArgumentException
+ ("Interpolation array cannot be empty.");
+ }
+ double out = 1;
+ for (int i = 0; i < x.length; i++) {
+ out *= (z - x[i]) / (i + 1);
+ }
+ return out;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolatorTest.java
new file mode 100644
index 0000000..a022d35
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingBicubicSplineInterpolatorTest.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.DimensionMismatchException;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the bicubic interpolator.
+ *
+ * @version $Revision: 1042336 $ $Date: 2010-12-05 13:40:48 +0100 (dim. 05 déc. 2010) $
+ * @deprecated To be removed in math 3.0 (when the class for which it is a test will also be removed).
+ */
+ at Deprecated
+public final class SmoothingBicubicSplineInterpolatorTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+
+ BivariateRealGridInterpolator interpolator = new SmoothingBicubicSplineInterpolator();
+
+ @SuppressWarnings("unused")
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ p = interpolator.interpolate(wxval, yval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[] wyval = new double[] {-4, -3, -1, -1};
+ try {
+ p = interpolator.interpolate(xval, wyval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[][] wzval = new double[xval.length][yval.length + 1];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wzval = new double[xval.length - 1][yval.length];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wzval = new double[xval.length][yval.length - 1];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ }
+
+ /**
+ * Test of interpolator for a plane.
+ * <p>
+ * z = 2 x - 3 y + 5
+ */
+ @Test
+ public void testPlane() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x - 3 * y + 5;
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new SmoothingBicubicSplineInterpolator();
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 0.3);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 0.3);
+ }
+
+ /**
+ * Test of interpolator for a paraboloid.
+ * <p>
+ * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+ */
+ @Test
+ public void testParaboloid() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new SmoothingBicubicSplineInterpolator();
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -2, -1, 0.5, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 5;
+ y = 0.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 1e-13);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 0.2);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 0.2);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolatorTest.java
new file mode 100644
index 0000000..34ba81e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolatorTest.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.analysis.BivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the smoothing bicubic interpolator.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class SmoothingPolynomialBicubicSplineInterpolatorTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+
+ BivariateRealGridInterpolator interpolator = new SmoothingPolynomialBicubicSplineInterpolator(0);
+
+ @SuppressWarnings("unused")
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ p = interpolator.interpolate(wxval, yval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[] wyval = new double[] {-4, -3, -1, -1};
+ try {
+ p = interpolator.interpolate(xval, wyval, zval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[][] wzval = new double[xval.length][yval.length + 1];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wzval = new double[xval.length - 1][yval.length];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wzval = new double[xval.length][yval.length - 1];
+ try {
+ p = interpolator.interpolate(xval, yval, wzval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ }
+
+ /**
+ * Test of interpolator for a plane.
+ * <p>
+ * z = 2 x - 3 y + 5
+ */
+ @Test
+ public void testPlane() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x - 3 * y + 5
+ + ((int) (FastMath.abs(5 * x + 3 * y)) % 2 == 0 ? 1 : -1);
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new SmoothingPolynomialBicubicSplineInterpolator(1);
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 2);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 2);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 2);
+ }
+
+ /**
+ * Test of interpolator for a paraboloid.
+ * <p>
+ * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+ */
+ @Test
+ public void testParaboloid() throws MathException {
+ BivariateRealFunction f = new BivariateRealFunction() {
+ public double value(double x, double y) {
+ return 2 * x * x - 3 * y * y + 4 * x * y - 5
+ + ((int) (FastMath.abs(5 * x + 3 * y)) % 2 == 0 ? 1 : -1);
+ }
+ };
+
+ BivariateRealGridInterpolator interpolator = new SmoothingPolynomialBicubicSplineInterpolator(4);
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -2, -1, 0.5, 2.5};
+ double[][] zval = new double[xval.length][yval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ zval[i][j] = f.value(xval[i], yval[j]);
+ }
+ }
+
+ BivariateRealFunction p = interpolator.interpolate(xval, yval, zval);
+ double x, y;
+ double expected, result;
+
+ x = 5;
+ y = 0.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("On sample point", expected, result, 2);
+
+ x = 4.5;
+ y = -1.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 2);
+
+ x = 3.5;
+ y = -3.5;
+ expected = f.value(x, y);
+ result = p.value(x, y);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 2);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/SplineInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/SplineInterpolatorTest.java
new file mode 100644
index 0000000..cff9c07
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/SplineInterpolatorTest.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.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.NonMonotonousSequenceException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test the SplineInterpolator.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class SplineInterpolatorTest {
+
+ /** error tolerance for spline interpolator value at knot points */
+ protected double knotTolerance = 1E-12;
+
+ /** error tolerance for interpolating polynomial coefficients */
+ protected double coefficientTolerance = 1E-6;
+
+ /** error tolerance for interpolated values -- high value is from sin test */
+ protected double interpolationTolerance = 1E-2;
+
+ @Test
+ public void testInterpolateLinearDegenerateTwoSegment()
+ throws Exception {
+ double x[] = { 0.0, 0.5, 1.0 };
+ double y[] = { 0.0, 0.5, 1.0 };
+ UnivariateRealInterpolator i = new SplineInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+ verifyConsistency((PolynomialSplineFunction) f, x);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 1d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+
+ // Check interpolation
+ Assert.assertEquals(0.0,f.value(0.0), interpolationTolerance);
+ Assert.assertEquals(0.4,f.value(0.4), interpolationTolerance);
+ Assert.assertEquals(1.0,f.value(1.0), interpolationTolerance);
+ }
+
+ @Test
+ public void testInterpolateLinearDegenerateThreeSegment()
+ throws Exception {
+ double x[] = { 0.0, 0.5, 1.0, 1.5 };
+ double y[] = { 0.0, 0.5, 1.0, 1.5 };
+ UnivariateRealInterpolator i = new SplineInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 1d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[2], 1d};
+ TestUtils.assertEquals(polynomials[2].getCoefficients(), target, coefficientTolerance);
+
+ // Check interpolation
+ Assert.assertEquals(0,f.value(0), interpolationTolerance);
+ Assert.assertEquals(1.4,f.value(1.4), interpolationTolerance);
+ Assert.assertEquals(1.5,f.value(1.5), interpolationTolerance);
+ }
+
+ @Test
+ public void testInterpolateLinear() throws Exception {
+ double x[] = { 0.0, 0.5, 1.0 };
+ double y[] = { 0.0, 0.5, 0.0 };
+ UnivariateRealInterpolator i = new SplineInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+ verifyConsistency((PolynomialSplineFunction) f, x);
+
+ // Verify coefficients using analytical values
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1.5d, 0d, -2d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 0d, -3d, 2d};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+ }
+
+ @Test
+ public void testInterpolateSin() throws Exception {
+ double x[] =
+ {
+ 0.0,
+ FastMath.PI / 6d,
+ FastMath.PI / 2d,
+ 5d * FastMath.PI / 6d,
+ FastMath.PI,
+ 7d * FastMath.PI / 6d,
+ 3d * FastMath.PI / 2d,
+ 11d * FastMath.PI / 6d,
+ 2.d * FastMath.PI };
+ double y[] = { 0d, 0.5d, 1d, 0.5d, 0d, -0.5d, -1d, -0.5d, 0d };
+ UnivariateRealInterpolator i = new SplineInterpolator();
+ UnivariateRealFunction f = i.interpolate(x, y);
+ verifyInterpolation(f, x, y);
+ verifyConsistency((PolynomialSplineFunction) f, x);
+
+ /* Check coefficients against values computed using R (version 1.8.1, Red Hat Linux 9)
+ *
+ * To replicate in R:
+ * x[1] <- 0
+ * x[2] <- pi / 6, etc, same for y[] (could use y <- scan() for y values)
+ * g <- splinefun(x, y, "natural")
+ * splinecoef <- eval(expression(z), envir = environment(g))
+ * print(splinecoef)
+ */
+ PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
+ double target[] = {y[0], 1.002676d, 0d, -0.17415829d};
+ TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[1], 8.594367e-01, -2.735672e-01, -0.08707914};
+ TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[2], 1.471804e-17,-5.471344e-01, 0.08707914};
+ TestUtils.assertEquals(polynomials[2].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[3], -8.594367e-01, -2.735672e-01, 0.17415829};
+ TestUtils.assertEquals(polynomials[3].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[4], -1.002676, 6.548562e-17, 0.17415829};
+ TestUtils.assertEquals(polynomials[4].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[5], -8.594367e-01, 2.735672e-01, 0.08707914};
+ TestUtils.assertEquals(polynomials[5].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[6], 3.466465e-16, 5.471344e-01, -0.08707914};
+ TestUtils.assertEquals(polynomials[6].getCoefficients(), target, coefficientTolerance);
+ target = new double[]{y[7], 8.594367e-01, 2.735672e-01, -0.17415829};
+ TestUtils.assertEquals(polynomials[7].getCoefficients(), target, coefficientTolerance);
+
+ //Check interpolation
+ Assert.assertEquals(FastMath.sqrt(2d) / 2d,f.value(FastMath.PI/4d),interpolationTolerance);
+ Assert.assertEquals(FastMath.sqrt(2d) / 2d,f.value(3d*FastMath.PI/4d),interpolationTolerance);
+ }
+
+ @Test
+ public void testIllegalArguments() throws MathException {
+ // Data set arrays of different size.
+ UnivariateRealInterpolator i = new SplineInterpolator();
+ try {
+ double xval[] = { 0.0, 1.0 };
+ double yval[] = { 0.0, 1.0, 2.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect data set array with different sizes.");
+ } catch (DimensionMismatchException iae) {
+ // Expected.
+ }
+ // X values not sorted.
+ try {
+ double xval[] = { 0.0, 1.0, 0.5 };
+ double yval[] = { 0.0, 1.0, 2.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect unsorted arguments.");
+ } catch (NonMonotonousSequenceException iae) {
+ // Expected.
+ }
+ // Not enough data to interpolate.
+ try {
+ double xval[] = { 0.0, 1.0 };
+ double yval[] = { 0.0, 1.0 };
+ i.interpolate(xval, yval);
+ Assert.fail("Failed to detect unsorted arguments.");
+ } catch (NumberIsTooSmallException iae) {
+ // Expected.
+ }
+ }
+
+ /**
+ * verifies that f(x[i]) = y[i] for i = 0..n-1 where n is common length.
+ */
+ protected void verifyInterpolation(UnivariateRealFunction f, double x[], double y[])
+ throws Exception{
+ for (int i = 0; i < x.length; i++) {
+ Assert.assertEquals(f.value(x[i]), y[i], knotTolerance);
+ }
+ }
+
+ /**
+ * Verifies that interpolating polynomials satisfy consistency requirement:
+ * adjacent polynomials must agree through two derivatives at knot points
+ */
+ protected void verifyConsistency(PolynomialSplineFunction f, double x[])
+ throws Exception {
+ PolynomialFunction polynomials[] = f.getPolynomials();
+ for (int i = 1; i < x.length - 2; i++) {
+ // evaluate polynomials and derivatives at x[i + 1]
+ Assert.assertEquals(polynomials[i].value(x[i +1] - x[i]), polynomials[i + 1].value(0), 0.1);
+ Assert.assertEquals(polynomials[i].derivative().value(x[i +1] - x[i]),
+ polynomials[i + 1].derivative().value(0), 0.5);
+ Assert.assertEquals(polynomials[i].polynomialDerivative().derivative().value(x[i +1] - x[i]),
+ polynomials[i + 1].polynomialDerivative().derivative().value(0), 0.5);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunctionTest.java
new file mode 100644
index 0000000..1d9943d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatingFunctionTest.java
@@ -0,0 +1,546 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FunctionEvaluationException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.analysis.TrivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the bicubic function.
+ *
+ * @version $Revision: 821626 $ $Date: 2009-10-04 23:57:30 +0200 (Sun, 04 Oct 2009) $
+ */
+public final class TricubicSplineInterpolatingFunctionTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 2.5};
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+
+ @SuppressWarnings("unused")
+ TrivariateRealFunction tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, fval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(wxval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ double[] wyval = new double[] {-4, -1, -1, 2.5};
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, wyval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ double[] wzval = new double[] {-12, -8, -9, -3, 0, 2.5};
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, wzval,
+ fval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ double[][][] wfval = new double[xval.length - 1][yval.length - 1][zval.length];
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ wfval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, wfval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, wfval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, wfval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ wfval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, wfval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, wfval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wfval = new double[xval.length][yval.length - 1][zval.length];
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ wfval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, wfval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, wfval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, wfval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ wfval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, wfval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, wfval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wfval = new double[xval.length][yval.length][zval.length - 1];
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ wfval, fval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, wfval, fval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, wfval, fval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, wfval,
+ fval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ wfval, fval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, wfval, fval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, wfval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ try {
+ tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, fval, fval, fval,
+ fval, fval, fval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ Assert.assertNotNull(tcf); // Avoid Findbugs "dead store" warning
+ }
+
+ /**
+ * Test for a plane.
+ * <p>
+ * f(x, y, z) = 2 x - 3 y - 4 z + 5
+ * </p>
+ */
+ @Test
+ public void testPlane() throws FunctionEvaluationException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 2.5};
+
+ // Function values
+ TrivariateRealFunction f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return 2 * x - 3 * y - 4 * z + 5;
+ }
+ };
+
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ fval[i][j][k] = f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+ // Partial derivatives with respect to x
+ double[][][] dFdX = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdX[i][j][k] = 2;
+ }
+ }
+ }
+ // Partial derivatives with respect to y
+ double[][][] dFdY = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdY[i][j][k] = -3;
+ }
+ }
+ }
+
+ // Partial derivatives with respect to z
+ double[][][] dFdZ = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdZ[i][j][k] = -4;
+ }
+ }
+ }
+ // Partial cross-derivatives
+ double[][][] d2FdXdY = new double[xval.length][yval.length][zval.length];
+ double[][][] d2FdXdZ = new double[xval.length][yval.length][zval.length];
+ double[][][] d2FdYdZ = new double[xval.length][yval.length][zval.length];
+ double[][][] d3FdXdYdZ = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ d2FdXdY[i][j][k] = 0;
+ d2FdXdZ[i][j][k] = 0;
+ d2FdYdZ[i][j][k] = 0;
+ d3FdXdYdZ[i][j][k] = 0;
+ }
+ }
+ }
+
+ TrivariateRealFunction tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, dFdX, dFdY, dFdZ,
+ d2FdXdY, d2FdXdZ, d2FdYdZ,
+ d3FdXdYdZ);
+ double x, y, z;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ z = 0;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("On sample point",
+ expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ z = -4.25;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (middle of the patch)",
+ expected, result, 0.3);
+
+ x = 3.5;
+ y = -3.5;
+ z = -10;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (border of the patch)",
+ expected, result, 0.3);
+ }
+
+ /**
+ * Sine wave.
+ * <p>
+ * f(x, y, z) = a cos [ω z - k<sub>y</sub> x - k<sub>y</sub> y]
+ * </p>
+ * with A = 0.2, ω = 0.5, k<sub>x</sub> = 2, k<sub>y</sub> = 1.
+ */
+ @Test
+ public void testWave() throws FunctionEvaluationException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 4};
+
+ final double a = 0.2;
+ final double omega = 0.5;
+ final double kx = 2;
+ final double ky = 1;
+
+ // Function values
+ TrivariateRealFunction f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.cos(omega * z - kx * x - ky * y);
+ }
+ };
+
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ fval[i][j][k] = f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial derivatives with respect to x
+ double[][][] dFdX = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction dFdX_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.sin(omega * z - kx * x - ky * y) * kx;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdX[i][j][k] = dFdX_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial derivatives with respect to y
+ double[][][] dFdY = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction dFdY_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.sin(omega * z - kx * x - ky * y) * ky;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdY[i][j][k] = dFdY_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial derivatives with respect to z
+ double[][][] dFdZ = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction dFdZ_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return -a * FastMath.sin(omega * z - kx * x - ky * y) * omega;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ dFdZ[i][j][k] = dFdZ_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial second derivatives w.r.t. (x, y)
+ double[][][] d2FdXdY = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction d2FdXdY_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return -a * FastMath.cos(omega * z - kx * x - ky * y) * kx * ky;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ d2FdXdY[i][j][k] = d2FdXdY_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial second derivatives w.r.t. (x, z)
+ double[][][] d2FdXdZ = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction d2FdXdZ_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.cos(omega * z - kx * x - ky * y) * kx * omega;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ d2FdXdZ[i][j][k] = d2FdXdZ_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial second derivatives w.r.t. (y, z)
+ double[][][] d2FdYdZ = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction d2FdYdZ_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.cos(omega * z - kx * x - ky * y) * ky * omega;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ d2FdYdZ[i][j][k] = d2FdYdZ_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ // Partial third derivatives
+ double[][][] d3FdXdYdZ = new double[xval.length][yval.length][zval.length];
+ TrivariateRealFunction d3FdXdYdZ_f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.sin(omega * z - kx * x - ky * y) * kx * ky * omega;
+ }
+ };
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ d3FdXdYdZ[i][j][k] = d3FdXdYdZ_f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ TrivariateRealFunction tcf = new TricubicSplineInterpolatingFunction(xval, yval, zval,
+ fval, dFdX, dFdY, dFdZ,
+ d2FdXdY, d2FdXdZ, d2FdYdZ,
+ d3FdXdYdZ);
+ double x, y, z;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ z = 0;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("On sample point",
+ expected, result, 1e-14);
+
+ x = 4.5;
+ y = -1.5;
+ z = -4.25;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (middle of the patch)",
+ expected, result, 0.1);
+
+ x = 3.5;
+ y = -3.5;
+ z = -10;
+ expected = f.value(x, y, z);
+ result = tcf.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (border of the patch)",
+ expected, result, 0.1);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatorTest.java b/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatorTest.java
new file mode 100644
index 0000000..a678694
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/interpolation/TricubicSplineInterpolatorTest.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.interpolation;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.analysis.TrivariateRealFunction;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Testcase for the tricubic interpolator.
+ *
+ * @version $Revision$ $Date$
+ */
+public final class TricubicSplineInterpolatorTest {
+ /**
+ * Test preconditions.
+ */
+ @Test
+ public void testPreconditions() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 2.5};
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+
+ TrivariateRealGridInterpolator interpolator = new TricubicSplineInterpolator();
+
+ @SuppressWarnings("unused")
+ TrivariateRealFunction p = interpolator.interpolate(xval, yval, zval, fval);
+
+ double[] wxval = new double[] {3, 2, 5, 6.5};
+ try {
+ p = interpolator.interpolate(wxval, yval, zval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[] wyval = new double[] {-4, -3, -1, -1};
+ try {
+ p = interpolator.interpolate(xval, wyval, zval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[] wzval = new double[] {-12, -8, -5.5, -3, -4, 2.5};
+ try {
+ p = interpolator.interpolate(xval, yval, wzval, fval);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ double[][][] wfval = new double[xval.length][yval.length + 1][zval.length];
+ try {
+ p = interpolator.interpolate(xval, yval, zval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wfval = new double[xval.length - 1][yval.length][zval.length];
+ try {
+ p = interpolator.interpolate(xval, yval, zval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ wfval = new double[xval.length][yval.length][zval.length - 1];
+ try {
+ p = interpolator.interpolate(xval, yval, zval, wfval);
+ Assert.fail("an exception should have been thrown");
+ } catch (DimensionMismatchException e) {
+ // Expected
+ }
+ }
+
+ /**
+ * Test of interpolator for a plane.
+ * <p>
+ * f(x, y, z) = 2 x - 3 y - z + 5
+ */
+ @Test
+ public void testPlane() throws MathException {
+ TrivariateRealFunction f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return 2 * x - 3 * y - z + 5;
+ }
+ };
+
+ TrivariateRealGridInterpolator interpolator = new TricubicSplineInterpolator();
+
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 2.5};
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ fval[i][j][k] = f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ TrivariateRealFunction p = interpolator.interpolate(xval, yval, zval, fval);
+ double x, y, z;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ z = 0;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("On sample point", expected, result, 1e-15);
+
+ x = 4.5;
+ y = -1.5;
+ z = -4.25;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("half-way between sample points (middle of the patch)", expected, result, 0.3);
+
+ x = 3.5;
+ y = -3.5;
+ z = -10;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("half-way between sample points (border of the patch)", expected, result, 0.3);
+ }
+
+ /**
+ * Test of interpolator for a sine wave.
+ * <p>
+ * <p>
+ * f(x, y, z) = a cos [ω z - k<sub>y</sub> x - k<sub>y</sub> y]
+ * </p>
+ * with A = 0.2, ω = 0.5, k<sub>x</sub> = 2, k<sub>y</sub> = 1.
+ */
+ @Test
+ public void testWave() throws MathException {
+ double[] xval = new double[] {3, 4, 5, 6.5};
+ double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+ double[] zval = new double[] {-12, -8, -5.5, -3, 0, 4};
+
+ final double a = 0.2;
+ final double omega = 0.5;
+ final double kx = 2;
+ final double ky = 1;
+
+ // Function values
+ TrivariateRealFunction f = new TrivariateRealFunction() {
+ public double value(double x, double y, double z) {
+ return a * FastMath.cos(omega * z - kx * x - ky * y);
+ }
+ };
+
+ double[][][] fval = new double[xval.length][yval.length][zval.length];
+ for (int i = 0; i < xval.length; i++) {
+ for (int j = 0; j < yval.length; j++) {
+ for (int k = 0; k < zval.length; k++) {
+ fval[i][j][k] = f.value(xval[i], yval[j], zval[k]);
+ }
+ }
+ }
+
+ TrivariateRealGridInterpolator interpolator = new TricubicSplineInterpolator();
+
+ TrivariateRealFunction p = interpolator.interpolate(xval, yval, zval, fval);
+ double x, y, z;
+ double expected, result;
+
+ x = 4;
+ y = -3;
+ z = 0;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("On sample point",
+ expected, result, 1e-12);
+
+ x = 4.5;
+ y = -1.5;
+ z = -4.25;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (middle of the patch)",
+ expected, result, 0.1);
+
+ x = 3.5;
+ y = -3.5;
+ z = -10;
+ expected = f.value(x, y, z);
+ result = p.value(x, y, z);
+ Assert.assertEquals("Half-way between sample points (border of the patch)",
+ expected, result, 0.1);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeFormTest.java b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeFormTest.java
new file mode 100644
index 0000000..54cfb1e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionLagrangeFormTest.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.analysis.polynomials;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Lagrange form of polynomial function.
+ * <p>
+ * We use n+1 points to interpolate a polynomial of degree n. This should
+ * give us the exact same polynomial as result. Thus we can use a very
+ * small tolerance to account only for round-off errors.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ */
+public final class PolynomialFunctionLagrangeFormTest extends TestCase {
+
+ /**
+ * Test of polynomial for the linear function.
+ */
+ public void testLinearFunction() throws FunctionEvaluationException {
+ PolynomialFunctionLagrangeForm p;
+ double c[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = 1.5x - 4
+ double x[] = { 0.0, 3.0 };
+ double y[] = { -4.0, 0.5 };
+ p = new PolynomialFunctionLagrangeForm(x, y);
+
+ z = 2.0; expected = -1.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 4.5; expected = 2.75; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 6.0; expected = 5.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(1, p.degree());
+
+ c = p.getCoefficients();
+ assertEquals(2, c.length);
+ assertEquals(-4.0, c[0], tolerance);
+ assertEquals(1.5, c[1], tolerance);
+ }
+
+ /**
+ * Test of polynomial for the quadratic function.
+ */
+ public void testQuadraticFunction() throws FunctionEvaluationException {
+ PolynomialFunctionLagrangeForm p;
+ double c[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = 2x^2 + 5x - 3 = (2x - 1)(x + 3)
+ double x[] = { 0.0, -1.0, 0.5 };
+ double y[] = { -3.0, -6.0, 0.0 };
+ p = new PolynomialFunctionLagrangeForm(x, y);
+
+ z = 1.0; expected = 4.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 2.5; expected = 22.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = -2.0; expected = -5.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(2, p.degree());
+
+ c = p.getCoefficients();
+ assertEquals(3, c.length);
+ assertEquals(-3.0, c[0], tolerance);
+ assertEquals(5.0, c[1], tolerance);
+ assertEquals(2.0, c[2], tolerance);
+ }
+
+ /**
+ * Test of polynomial for the quintic function.
+ */
+ public void testQuinticFunction() throws FunctionEvaluationException {
+ PolynomialFunctionLagrangeForm p;
+ double c[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = x^5 - x^4 - 7x^3 + x^2 + 6x = x(x^2 - 1)(x + 2)(x - 3)
+ double x[] = { 1.0, -1.0, 2.0, 3.0, -3.0, 0.5 };
+ double y[] = { 0.0, 0.0, -24.0, 0.0, -144.0, 2.34375 };
+ p = new PolynomialFunctionLagrangeForm(x, y);
+
+ z = 0.0; expected = 0.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = -2.0; expected = 0.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 4.0; expected = 360.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(5, p.degree());
+
+ c = p.getCoefficients();
+ assertEquals(6, c.length);
+ assertEquals(0.0, c[0], tolerance);
+ assertEquals(6.0, c[1], tolerance);
+ assertEquals(1.0, c[2], tolerance);
+ assertEquals(-7.0, c[3], tolerance);
+ assertEquals(-1.0, c[4], tolerance);
+ assertEquals(1.0, c[5], tolerance);
+ }
+
+ /**
+ * Test of parameters for the polynomial.
+ */
+ public void testParameters() throws Exception {
+
+ try {
+ // bad input array length
+ double x[] = { 1.0 };
+ double y[] = { 2.0 };
+ new PolynomialFunctionLagrangeForm(x, y);
+ fail("Expecting IllegalArgumentException - bad input array length");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // mismatch input arrays
+ double x[] = { 1.0, 2.0, 3.0, 4.0 };
+ double y[] = { 0.0, -4.0, -24.0 };
+ new PolynomialFunctionLagrangeForm(x, y);
+ fail("Expecting IllegalArgumentException - mismatch input arrays");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonFormTest.java b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonFormTest.java
new file mode 100644
index 0000000..d47c1b1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionNewtonFormTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FunctionEvaluationException;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Newton form of polynomial function.
+ * <p>
+ * The small tolerance number is used only to account for round-off errors.
+ *
+ * @version $Revision: 1073498 $ $Date: 2011-02-22 21:57:26 +0100 (mar. 22 févr. 2011) $
+ */
+public final class PolynomialFunctionNewtonFormTest extends TestCase {
+
+ /**
+ * Test of polynomial for the linear function.
+ */
+ public void testLinearFunction() throws FunctionEvaluationException {
+ PolynomialFunctionNewtonForm p;
+ double coefficients[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = 1.5x - 4 = 2 + 1.5(x-4)
+ double a[] = { 2.0, 1.5 };
+ double c[] = { 4.0 };
+ p = new PolynomialFunctionNewtonForm(a, c);
+
+ z = 2.0; expected = -1.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 4.5; expected = 2.75; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 6.0; expected = 5.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(1, p.degree());
+
+ coefficients = p.getCoefficients();
+ assertEquals(2, coefficients.length);
+ assertEquals(-4.0, coefficients[0], tolerance);
+ assertEquals(1.5, coefficients[1], tolerance);
+ }
+
+ /**
+ * Test of polynomial for the quadratic function.
+ */
+ public void testQuadraticFunction() throws FunctionEvaluationException {
+ PolynomialFunctionNewtonForm p;
+ double coefficients[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = 2x^2 + 5x - 3 = 4 + 3(x-1) + 2(x-1)(x+2)
+ double a[] = { 4.0, 3.0, 2.0 };
+ double c[] = { 1.0, -2.0 };
+ p = new PolynomialFunctionNewtonForm(a, c);
+
+ z = 1.0; expected = 4.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 2.5; expected = 22.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = -2.0; expected = -5.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(2, p.degree());
+
+ coefficients = p.getCoefficients();
+ assertEquals(3, coefficients.length);
+ assertEquals(-3.0, coefficients[0], tolerance);
+ assertEquals(5.0, coefficients[1], tolerance);
+ assertEquals(2.0, coefficients[2], tolerance);
+ }
+
+ /**
+ * Test of polynomial for the quintic function.
+ */
+ public void testQuinticFunction() throws FunctionEvaluationException {
+ PolynomialFunctionNewtonForm p;
+ double coefficients[], z, expected, result, tolerance = 1E-12;
+
+ // p(x) = x^5 - x^4 - 7x^3 + x^2 + 6x
+ // = 6x - 6x^2 -6x^2(x-1) + x^2(x-1)(x+1) + x^2(x-1)(x+1)(x-2)
+ double a[] = { 0.0, 6.0, -6.0, -6.0, 1.0, 1.0 };
+ double c[] = { 0.0, 0.0, 1.0, -1.0, 2.0 };
+ p = new PolynomialFunctionNewtonForm(a, c);
+
+ z = 0.0; expected = 0.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = -2.0; expected = 0.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ z = 4.0; expected = 360.0; result = p.value(z);
+ assertEquals(expected, result, tolerance);
+
+ assertEquals(5, p.degree());
+
+ coefficients = p.getCoefficients();
+ assertEquals(6, coefficients.length);
+ assertEquals(0.0, coefficients[0], tolerance);
+ assertEquals(6.0, coefficients[1], tolerance);
+ assertEquals(1.0, coefficients[2], tolerance);
+ assertEquals(-7.0, coefficients[3], tolerance);
+ assertEquals(-1.0, coefficients[4], tolerance);
+ assertEquals(1.0, coefficients[5], tolerance);
+ }
+
+ /**
+ * Test of parameters for the polynomial.
+ */
+ public void testParameters() throws Exception {
+
+ try {
+ // bad input array length
+ double a[] = { 1.0 };
+ double c[] = { 2.0 };
+ new PolynomialFunctionNewtonForm(a, c);
+ fail("Expecting IllegalArgumentException - bad input array length");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // mismatch input arrays
+ double a[] = { 1.0, 2.0, 3.0, 4.0 };
+ double c[] = { 4.0, 3.0, 2.0, 1.0 };
+ new PolynomialFunctionNewtonForm(a, c);
+ fail("Expecting IllegalArgumentException - mismatch input arrays");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionTest.java
new file mode 100644
index 0000000..fb971b1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialFunctionTest.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.analysis.polynomials;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+// junit
+import junit.framework.TestCase;
+
+/**
+ * Tests the PolynomialFunction implementation of a UnivariateRealFunction.
+ *
+ * @version $Revision: 1070725 $
+ * @author Matt Cliff <matt at mattcliff.com>
+ */
+public final class PolynomialFunctionTest extends TestCase {
+
+ /** Error tolerance for tests */
+ protected double tolerance = 1.0e-12;
+
+ /**
+ * tests the value of a constant polynomial.
+ *
+ * <p>value of this is 2.5 everywhere.</p>
+ */
+ public void testConstants() throws Exception {
+ double[] c = { 2.5 };
+ PolynomialFunction f = new PolynomialFunction( c );
+
+ // verify that we are equal to c[0] at several (nonsymmetric) places
+ assertEquals( f.value( 0.0), c[0], tolerance );
+ assertEquals( f.value( -1.0), c[0], tolerance );
+ assertEquals( f.value( -123.5), c[0], tolerance );
+ assertEquals( f.value( 3.0), c[0], tolerance );
+ assertEquals( f.value( 456.89), c[0], tolerance );
+
+ assertEquals(f.degree(), 0);
+ assertEquals(f.derivative().value(0), 0, tolerance);
+
+ assertEquals(f.polynomialDerivative().derivative().value(0), 0, tolerance);
+ }
+
+ /**
+ * tests the value of a linear polynomial.
+ *
+ * <p>This will test the function f(x) = 3*x - 1.5</p>
+ * <p>This will have the values
+ * <tt>f(0.0) = -1.5, f(-1.0) = -4.5, f(-2.5) = -9.0,
+ * f(0.5) = 0.0, f(1.5) = 3.0</tt> and <tt>f(3.0) = 7.5</tt>
+ * </p>
+ */
+ public void testLinear() throws Exception {
+ double[] c = { -1.5, 3.0 };
+ PolynomialFunction f = new PolynomialFunction( c );
+
+ // verify that we are equal to c[0] when x=0
+ assertEquals( f.value( 0.0), c[0], tolerance );
+
+ // now check a few other places
+ assertEquals( -4.5, f.value( -1.0), tolerance );
+ assertEquals( -9.0, f.value( -2.5), tolerance );
+ assertEquals( 0.0, f.value( 0.5), tolerance );
+ assertEquals( 3.0, f.value( 1.5), tolerance );
+ assertEquals( 7.5, f.value( 3.0), tolerance );
+
+ assertEquals(f.degree(), 1);
+
+ assertEquals(f.polynomialDerivative().derivative().value(0), 0, tolerance);
+
+ }
+
+
+ /**
+ * Tests a second order polynomial.
+ * <p> This will test the function f(x) = 2x^2 - 3x -2 = (2x+1)(x-2)</p>
+ *
+ */
+ public void testQuadratic() {
+ double[] c = { -2.0, -3.0, 2.0 };
+ PolynomialFunction f = new PolynomialFunction( c );
+
+ // verify that we are equal to c[0] when x=0
+ assertEquals( f.value( 0.0), c[0], tolerance );
+
+ // now check a few other places
+ assertEquals( 0.0, f.value( -0.5), tolerance );
+ assertEquals( 0.0, f.value( 2.0), tolerance );
+ assertEquals( -2.0, f.value( 1.5), tolerance );
+ assertEquals( 7.0, f.value( -1.5), tolerance );
+ assertEquals( 265.5312, f.value( 12.34), tolerance );
+
+ }
+
+
+ /**
+ * This will test the quintic function
+ * f(x) = x^2(x-5)(x+3)(x-1) = x^5 - 3x^4 -13x^3 + 15x^2</p>
+ *
+ */
+ public void testQuintic() {
+ double[] c = { 0.0, 0.0, 15.0, -13.0, -3.0, 1.0 };
+ PolynomialFunction f = new PolynomialFunction( c );
+
+ // verify that we are equal to c[0] when x=0
+ assertEquals( f.value( 0.0), c[0], tolerance );
+
+ // now check a few other places
+ assertEquals( 0.0, f.value( 5.0), tolerance );
+ assertEquals( 0.0, f.value( 1.0), tolerance );
+ assertEquals( 0.0, f.value( -3.0), tolerance );
+ assertEquals( 54.84375, f.value( -1.5), tolerance );
+ assertEquals( -8.06637, f.value( 1.3), tolerance );
+
+ assertEquals(f.degree(), 5);
+
+ }
+
+
+ /**
+ * tests the firstDerivative function by comparison
+ *
+ * <p>This will test the functions
+ * <tt>f(x) = x^3 - 2x^2 + 6x + 3, g(x) = 3x^2 - 4x + 6</tt>
+ * and <tt>h(x) = 6x - 4</tt>
+ */
+ public void testfirstDerivativeComparison() throws Exception {
+ double[] f_coeff = { 3.0, 6.0, -2.0, 1.0 };
+ double[] g_coeff = { 6.0, -4.0, 3.0 };
+ double[] h_coeff = { -4.0, 6.0 };
+
+ PolynomialFunction f = new PolynomialFunction( f_coeff );
+ PolynomialFunction g = new PolynomialFunction( g_coeff );
+ PolynomialFunction h = new PolynomialFunction( h_coeff );
+
+ // compare f' = g
+ assertEquals( f.derivative().value(0.0), g.value(0.0), tolerance );
+ assertEquals( f.derivative().value(1.0), g.value(1.0), tolerance );
+ assertEquals( f.derivative().value(100.0), g.value(100.0), tolerance );
+ assertEquals( f.derivative().value(4.1), g.value(4.1), tolerance );
+ assertEquals( f.derivative().value(-3.25), g.value(-3.25), tolerance );
+
+ // compare g' = h
+ assertEquals( g.derivative().value(FastMath.PI), h.value(FastMath.PI), tolerance );
+ assertEquals( g.derivative().value(FastMath.E), h.value(FastMath.E), tolerance );
+
+ }
+
+ public void testString() {
+ PolynomialFunction p = new PolynomialFunction(new double[] { -5.0, 3.0, 1.0 });
+ checkPolynomial(p, "-5.0 + 3.0 x + x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 0.0, -2.0, 3.0 }),
+ "-2.0 x + 3.0 x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 1.0, -2.0, 3.0 }),
+ "1.0 - 2.0 x + 3.0 x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 0.0, 2.0, 3.0 }),
+ "2.0 x + 3.0 x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 1.0, 2.0, 3.0 }),
+ "1.0 + 2.0 x + 3.0 x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 1.0, 0.0, 3.0 }),
+ "1.0 + 3.0 x^2");
+ checkPolynomial(new PolynomialFunction(new double[] { 0.0 }),
+ "0");
+ }
+
+ public void testAddition() {
+
+ PolynomialFunction p1 = new PolynomialFunction(new double[] { -2.0, 1.0 });
+ PolynomialFunction p2 = new PolynomialFunction(new double[] { 2.0, -1.0, 0.0 });
+ checkNullPolynomial(p1.add(p2));
+
+ p2 = p1.add(p1);
+ checkPolynomial(p2, "-4.0 + 2.0 x");
+
+ p1 = new PolynomialFunction(new double[] { 1.0, -4.0, 2.0 });
+ p2 = new PolynomialFunction(new double[] { -1.0, 3.0, -2.0 });
+ p1 = p1.add(p2);
+ assertEquals(1, p1.degree());
+ checkPolynomial(p1, "-x");
+
+ }
+
+ public void testSubtraction() {
+
+ PolynomialFunction p1 = new PolynomialFunction(new double[] { -2.0, 1.0 });
+ checkNullPolynomial(p1.subtract(p1));
+
+ PolynomialFunction p2 = new PolynomialFunction(new double[] { -2.0, 6.0 });
+ p2 = p2.subtract(p1);
+ checkPolynomial(p2, "5.0 x");
+
+ p1 = new PolynomialFunction(new double[] { 1.0, -4.0, 2.0 });
+ p2 = new PolynomialFunction(new double[] { -1.0, 3.0, 2.0 });
+ p1 = p1.subtract(p2);
+ assertEquals(1, p1.degree());
+ checkPolynomial(p1, "2.0 - 7.0 x");
+
+ }
+
+ public void testMultiplication() {
+
+ PolynomialFunction p1 = new PolynomialFunction(new double[] { -3.0, 2.0 });
+ PolynomialFunction p2 = new PolynomialFunction(new double[] { 3.0, 2.0, 1.0 });
+ checkPolynomial(p1.multiply(p2), "-9.0 + x^2 + 2.0 x^3");
+
+ p1 = new PolynomialFunction(new double[] { 0.0, 1.0 });
+ p2 = p1;
+ for (int i = 2; i < 10; ++i) {
+ p2 = p2.multiply(p1);
+ checkPolynomial(p2, "x^" + i);
+ }
+
+ }
+
+ public void testSerial() {
+ PolynomialFunction p2 = new PolynomialFunction(new double[] { 3.0, 2.0, 1.0 });
+ assertEquals(p2, TestUtils.serializeAndRecover(p2));
+ }
+
+ /**
+ * tests the firstDerivative function by comparison
+ *
+ * <p>This will test the functions
+ * <tt>f(x) = x^3 - 2x^2 + 6x + 3, g(x) = 3x^2 - 4x + 6</tt>
+ * and <tt>h(x) = 6x - 4</tt>
+ */
+ public void testMath341() throws Exception {
+ double[] f_coeff = { 3.0, 6.0, -2.0, 1.0 };
+ double[] g_coeff = { 6.0, -4.0, 3.0 };
+ double[] h_coeff = { -4.0, 6.0 };
+
+ PolynomialFunction f = new PolynomialFunction( f_coeff );
+ PolynomialFunction g = new PolynomialFunction( g_coeff );
+ PolynomialFunction h = new PolynomialFunction( h_coeff );
+
+ // compare f' = g
+ assertEquals( f.derivative().value(0.0), g.value(0.0), tolerance );
+ assertEquals( f.derivative().value(1.0), g.value(1.0), tolerance );
+ assertEquals( f.derivative().value(100.0), g.value(100.0), tolerance );
+ assertEquals( f.derivative().value(4.1), g.value(4.1), tolerance );
+ assertEquals( f.derivative().value(-3.25), g.value(-3.25), tolerance );
+
+ // compare g' = h
+ assertEquals( g.derivative().value(FastMath.PI), h.value(FastMath.PI), tolerance );
+ assertEquals( g.derivative().value(FastMath.E), h.value(FastMath.E), tolerance );
+ }
+
+ public void checkPolynomial(PolynomialFunction p, String reference) {
+ assertEquals(reference, p.toString());
+ }
+
+ private void checkNullPolynomial(PolynomialFunction p) {
+ for (double coefficient : p.getCoefficients()) {
+ assertEquals(0.0, coefficient, 1.0e-15);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunctionTest.java b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunctionTest.java
new file mode 100644
index 0000000..1b2f2dc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialSplineFunctionTest.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.analysis.polynomials;
+
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+/**
+ * Tests the PolynomialSplineFunction implementation.
+ *
+ * @version $Revision: 1042508 $
+ */
+public class PolynomialSplineFunctionTest extends TestCase {
+
+ /** Error tolerance for tests */
+ protected double tolerance = 1.0e-12;
+
+ /**
+ * Quadratic polynomials used in tests:
+ *
+ * x^2 + x [-1, 0)
+ * x^2 + x + 2 [0, 1)
+ * x^2 + x + 4 [1, 2)
+ *
+ * Defined so that evaluation using PolynomialSplineFunction evaluation
+ * algorithm agrees at knot point boundaries.
+ */
+ protected PolynomialFunction[] polynomials = {
+ new PolynomialFunction(new double[] {0d, 1d, 1d}),
+ new PolynomialFunction(new double[] {2d, 1d, 1d}),
+ new PolynomialFunction(new double[] {4d, 1d, 1d})
+ };
+
+ /** Knot points */
+ protected double[] knots = {-1, 0, 1, 2};
+
+ /** Derivative of test polynomials -- 2x + 1 */
+ protected PolynomialFunction dp =
+ new PolynomialFunction(new double[] {1d, 2d});
+
+
+ public void testConstructor() {
+ PolynomialSplineFunction spline =
+ new PolynomialSplineFunction(knots, polynomials);
+ assertTrue(Arrays.equals(knots, spline.getKnots()));
+ assertEquals(1d, spline.getPolynomials()[0].getCoefficients()[2], 0);
+ assertEquals(3, spline.getN());
+
+ try { // too few knots
+ new PolynomialSplineFunction(new double[] {0}, polynomials);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try { // too many knots
+ new PolynomialSplineFunction(new double[] {0,1,2,3,4}, polynomials);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try { // knots not increasing
+ new PolynomialSplineFunction(new double[] {0,1, 3, 2}, polynomials);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testValues() throws Exception {
+ PolynomialSplineFunction spline =
+ new PolynomialSplineFunction(knots, polynomials);
+ UnivariateRealFunction dSpline = spline.derivative();
+
+ /**
+ * interior points -- spline value at x should equal p(x - knot)
+ * where knot is the largest knot point less than or equal to x and p
+ * is the polynomial defined over the knot segment to which x belongs.
+ */
+ double x = -1;
+ int index = 0;
+ for (int i = 0; i < 10; i++) {
+ x+=0.25;
+ index = findKnot(knots, x);
+ assertEquals("spline function evaluation failed for x=" + x,
+ polynomials[index].value(x - knots[index]), spline.value(x), tolerance);
+ assertEquals("spline derivative evaluation failed for x=" + x,
+ dp.value(x - knots[index]), dSpline.value(x), tolerance);
+ }
+
+ // knot points -- centering should zero arguments
+ for (int i = 0; i < 3; i++) {
+ assertEquals("spline function evaluation failed for knot=" + knots[i],
+ polynomials[i].value(0), spline.value(knots[i]), tolerance);
+ assertEquals("spline function evaluation failed for knot=" + knots[i],
+ dp.value(0), dSpline.value(knots[i]), tolerance);
+ }
+
+ try { //outside of domain -- under min
+ x = spline.value(-1.5);
+ fail("Expecting ArgumentOutsideDomainException");
+ } catch (ArgumentOutsideDomainException ex) {
+ // expected
+ }
+
+ try { //outside of domain -- over max
+ x = spline.value(2.5);
+ fail("Expecting ArgumentOutsideDomainException");
+ } catch (ArgumentOutsideDomainException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Do linear search to find largest knot point less than or equal to x.
+ * Implementation does binary search.
+ */
+ protected int findKnot(double[] knots, double x) {
+ if (x < knots[0] || x >= knots[knots.length -1]) {
+ throw new IllegalArgumentException("x is out of range");
+ }
+ for (int i = 0; i < knots.length; i++) {
+ if (knots[i] > x) {
+ return i -1;
+ }
+ }
+ throw new IllegalArgumentException("x is out of range");
+ }
+}
+
diff --git a/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtilsTest.java b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtilsTest.java
new file mode 100644
index 0000000..f0d84e6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/polynomials/PolynomialsUtilsTest.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.polynomials;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the PolynomialsUtils class.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class PolynomialsUtilsTest extends TestCase {
+
+ public void testFirstChebyshevPolynomials() {
+
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(3), "-3.0 x + 4.0 x^3");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(2), "-1.0 + 2.0 x^2");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(1), "x");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(0), "1.0");
+
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(7), "-7.0 x + 56.0 x^3 - 112.0 x^5 + 64.0 x^7");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(6), "-1.0 + 18.0 x^2 - 48.0 x^4 + 32.0 x^6");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(5), "5.0 x - 20.0 x^3 + 16.0 x^5");
+ checkPolynomial(PolynomialsUtils.createChebyshevPolynomial(4), "1.0 - 8.0 x^2 + 8.0 x^4");
+
+ }
+
+ public void testChebyshevBounds() {
+ for (int k = 0; k < 12; ++k) {
+ PolynomialFunction Tk = PolynomialsUtils.createChebyshevPolynomial(k);
+ for (double x = -1.0; x <= 1.0; x += 0.02) {
+ assertTrue(k + " " + Tk.value(x), FastMath.abs(Tk.value(x)) < (1.0 + 1.0e-12));
+ }
+ }
+ }
+
+ public void testChebyshevDifferentials() {
+ for (int k = 0; k < 12; ++k) {
+
+ PolynomialFunction Tk0 = PolynomialsUtils.createChebyshevPolynomial(k);
+ PolynomialFunction Tk1 = Tk0.polynomialDerivative();
+ PolynomialFunction Tk2 = Tk1.polynomialDerivative();
+
+ PolynomialFunction g0 = new PolynomialFunction(new double[] { k * k });
+ PolynomialFunction g1 = new PolynomialFunction(new double[] { 0, -1});
+ PolynomialFunction g2 = new PolynomialFunction(new double[] { 1, 0, -1 });
+
+ PolynomialFunction Tk0g0 = Tk0.multiply(g0);
+ PolynomialFunction Tk1g1 = Tk1.multiply(g1);
+ PolynomialFunction Tk2g2 = Tk2.multiply(g2);
+
+ checkNullPolynomial(Tk0g0.add(Tk1g1.add(Tk2g2)));
+
+ }
+ }
+
+ public void testFirstHermitePolynomials() {
+
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(3), "-12.0 x + 8.0 x^3");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(2), "-2.0 + 4.0 x^2");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(1), "2.0 x");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(0), "1.0");
+
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(7), "-1680.0 x + 3360.0 x^3 - 1344.0 x^5 + 128.0 x^7");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(6), "-120.0 + 720.0 x^2 - 480.0 x^4 + 64.0 x^6");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(5), "120.0 x - 160.0 x^3 + 32.0 x^5");
+ checkPolynomial(PolynomialsUtils.createHermitePolynomial(4), "12.0 - 48.0 x^2 + 16.0 x^4");
+
+ }
+
+ public void testHermiteDifferentials() {
+ for (int k = 0; k < 12; ++k) {
+
+ PolynomialFunction Hk0 = PolynomialsUtils.createHermitePolynomial(k);
+ PolynomialFunction Hk1 = Hk0.polynomialDerivative();
+ PolynomialFunction Hk2 = Hk1.polynomialDerivative();
+
+ PolynomialFunction g0 = new PolynomialFunction(new double[] { 2 * k });
+ PolynomialFunction g1 = new PolynomialFunction(new double[] { 0, -2 });
+ PolynomialFunction g2 = new PolynomialFunction(new double[] { 1 });
+
+ PolynomialFunction Hk0g0 = Hk0.multiply(g0);
+ PolynomialFunction Hk1g1 = Hk1.multiply(g1);
+ PolynomialFunction Hk2g2 = Hk2.multiply(g2);
+
+ checkNullPolynomial(Hk0g0.add(Hk1g1.add(Hk2g2)));
+
+ }
+ }
+
+ public void testFirstLaguerrePolynomials() {
+
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(3), 6l, "6.0 - 18.0 x + 9.0 x^2 - x^3");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(2), 2l, "2.0 - 4.0 x + x^2");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(1), 1l, "1.0 - x");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(0), 1l, "1.0");
+
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(7), 5040l,
+ "5040.0 - 35280.0 x + 52920.0 x^2 - 29400.0 x^3"
+ + " + 7350.0 x^4 - 882.0 x^5 + 49.0 x^6 - x^7");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(6), 720l,
+ "720.0 - 4320.0 x + 5400.0 x^2 - 2400.0 x^3 + 450.0 x^4"
+ + " - 36.0 x^5 + x^6");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(5), 120l,
+ "120.0 - 600.0 x + 600.0 x^2 - 200.0 x^3 + 25.0 x^4 - x^5");
+ checkPolynomial(PolynomialsUtils.createLaguerrePolynomial(4), 24l,
+ "24.0 - 96.0 x + 72.0 x^2 - 16.0 x^3 + x^4");
+
+ }
+
+ public void testLaguerreDifferentials() {
+ for (int k = 0; k < 12; ++k) {
+
+ PolynomialFunction Lk0 = PolynomialsUtils.createLaguerrePolynomial(k);
+ PolynomialFunction Lk1 = Lk0.polynomialDerivative();
+ PolynomialFunction Lk2 = Lk1.polynomialDerivative();
+
+ PolynomialFunction g0 = new PolynomialFunction(new double[] { k });
+ PolynomialFunction g1 = new PolynomialFunction(new double[] { 1, -1 });
+ PolynomialFunction g2 = new PolynomialFunction(new double[] { 0, 1 });
+
+ PolynomialFunction Lk0g0 = Lk0.multiply(g0);
+ PolynomialFunction Lk1g1 = Lk1.multiply(g1);
+ PolynomialFunction Lk2g2 = Lk2.multiply(g2);
+
+ checkNullPolynomial(Lk0g0.add(Lk1g1.add(Lk2g2)));
+
+ }
+ }
+
+ public void testFirstLegendrePolynomials() {
+
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(3), 2l, "-3.0 x + 5.0 x^3");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(2), 2l, "-1.0 + 3.0 x^2");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(1), 1l, "x");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(0), 1l, "1.0");
+
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(7), 16l, "-35.0 x + 315.0 x^3 - 693.0 x^5 + 429.0 x^7");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(6), 16l, "-5.0 + 105.0 x^2 - 315.0 x^4 + 231.0 x^6");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(5), 8l, "15.0 x - 70.0 x^3 + 63.0 x^5");
+ checkPolynomial(PolynomialsUtils.createLegendrePolynomial(4), 8l, "3.0 - 30.0 x^2 + 35.0 x^4");
+
+ }
+
+ public void testLegendreDifferentials() {
+ for (int k = 0; k < 12; ++k) {
+
+ PolynomialFunction Pk0 = PolynomialsUtils.createLegendrePolynomial(k);
+ PolynomialFunction Pk1 = Pk0.polynomialDerivative();
+ PolynomialFunction Pk2 = Pk1.polynomialDerivative();
+
+ PolynomialFunction g0 = new PolynomialFunction(new double[] { k * (k + 1) });
+ PolynomialFunction g1 = new PolynomialFunction(new double[] { 0, -2 });
+ PolynomialFunction g2 = new PolynomialFunction(new double[] { 1, 0, -1 });
+
+ PolynomialFunction Pk0g0 = Pk0.multiply(g0);
+ PolynomialFunction Pk1g1 = Pk1.multiply(g1);
+ PolynomialFunction Pk2g2 = Pk2.multiply(g2);
+
+ checkNullPolynomial(Pk0g0.add(Pk1g1.add(Pk2g2)));
+
+ }
+ }
+
+ public void testHighDegreeLegendre() {
+ PolynomialsUtils.createLegendrePolynomial(40);
+ double[] l40 = PolynomialsUtils.createLegendrePolynomial(40).getCoefficients();
+ double denominator = 274877906944.0;
+ double[] numerators = new double[] {
+ +34461632205.0, -28258538408100.0, +3847870979902950.0, -207785032914759300.0,
+ +5929294332103310025.0, -103301483474866556880.0, +1197358103913226000200.0, -9763073770369381232400.0,
+ +58171647881784229843050.0, -260061484647976556945400.0, +888315281771246239250340.0, -2345767627188139419665400.0,
+ +4819022625419112503443050.0, -7710436200670580005508880.0, +9566652323054238154983240.0, -9104813935044723209570256.0,
+ +6516550296251767619752905.0, -3391858621221953912598660.0, +1211378079007840683070950.0, -265365894974690562152100.0,
+ +26876802183334044115405.0
+ };
+ for (int i = 0; i < l40.length; ++i) {
+ if (i % 2 == 0) {
+ double ci = numerators[i / 2] / denominator;
+ assertEquals(ci, l40[i], FastMath.abs(ci) * 1.0e-15);
+ } else {
+ assertEquals(0.0, l40[i], 0.0);
+ }
+ }
+ }
+
+ private void checkPolynomial(PolynomialFunction p, long denominator, String reference) {
+ PolynomialFunction q = new PolynomialFunction(new double[] { denominator});
+ assertEquals(reference, p.multiply(q).toString());
+ }
+
+ private void checkPolynomial(PolynomialFunction p, String reference) {
+ assertEquals(reference, p.toString());
+ }
+
+ private void checkNullPolynomial(PolynomialFunction p) {
+ for (double coefficient : p.getCoefficients()) {
+ assertEquals(0.0, coefficient, 1.0e-13);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/BisectionSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/BisectionSolverTest.java
new file mode 100644
index 0000000..a8ba41c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/BisectionSolverTest.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class BisectionSolverTest extends TestCase {
+
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ double result;
+
+ UnivariateRealSolver solver = new BisectionSolver(f);
+ result = solver.solve(3, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(1, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ }
+
+ public void testSinZero() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ double result;
+
+ UnivariateRealSolver solver = new BisectionSolver();
+ result = solver.solve(f, 3, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 1, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ }
+
+ public void testQuinticZero() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ double result;
+
+ UnivariateRealSolver solver = new BisectionSolver();
+ result = solver.solve(f, -0.2, 0.2);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, -0.1, 0.3);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, -0.3, 0.45);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.3, 0.7);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.2, 0.6);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.05, 0.95);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 1.25);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.8, 1.2);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 1.75);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.55, 1.45);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 5);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ assertEquals(result, solver.getResult(), 0);
+ assertTrue(solver.getIterationCount() > 0);
+ }
+
+ public void testMath369() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new BisectionSolver();
+ assertEquals(FastMath.PI, solver.solve(f, 3.0, 3.2, 3.1), solver.getAbsoluteAccuracy());
+ }
+
+ /**
+ *
+ */
+ public void testSetFunctionValueAccuracy(){
+ double expected = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ solver.setFunctionValueAccuracy(expected);
+ assertEquals(expected, solver.getFunctionValueAccuracy(), 1.0e-2);
+ }
+
+ /**
+ *
+ */
+ public void testResetFunctionValueAccuracy(){
+ double newValue = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ double oldValue = solver.getFunctionValueAccuracy();
+ solver.setFunctionValueAccuracy(newValue);
+ solver.resetFunctionValueAccuracy();
+ assertEquals(oldValue, solver.getFunctionValueAccuracy(), 1.0e-2);
+ }
+
+ /**
+ *
+ */
+ public void testSetAbsoluteAccuracy(){
+ double expected = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ solver.setAbsoluteAccuracy(expected);
+ assertEquals(expected, solver.getAbsoluteAccuracy(), 1.0e-2);
+ }
+
+ /**
+ *
+ */
+ public void testResetAbsoluteAccuracy(){
+ double newValue = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ double oldValue = solver.getAbsoluteAccuracy();
+ solver.setAbsoluteAccuracy(newValue);
+ solver.resetAbsoluteAccuracy();
+ assertEquals(oldValue, solver.getAbsoluteAccuracy(), 1.0e-2);
+ }
+
+ /**
+ *
+ */
+ public void testSetMaximalIterationCount(){
+ int expected = 100;
+ UnivariateRealSolver solver = new BisectionSolver();
+ solver.setMaximalIterationCount(expected);
+ assertEquals(expected, solver.getMaximalIterationCount());
+ }
+
+ /**
+ *
+ */
+ public void testResetMaximalIterationCount(){
+ int newValue = 10000;
+ UnivariateRealSolver solver = new BisectionSolver();
+ int oldValue = solver.getMaximalIterationCount();
+ solver.setMaximalIterationCount(newValue);
+ solver.resetMaximalIterationCount();
+ assertEquals(oldValue, solver.getMaximalIterationCount());
+ }
+
+ /**
+ *
+ */
+ public void testSetRelativeAccuracy(){
+ double expected = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ solver.setRelativeAccuracy(expected);
+ assertEquals(expected, solver.getRelativeAccuracy(), 1.0e-2);
+ }
+
+ /**
+ *
+ */
+ public void testResetRelativeAccuracy(){
+ double newValue = 1.0e-2;
+ UnivariateRealSolver solver = new BisectionSolver();
+ double oldValue = solver.getRelativeAccuracy();
+ solver.setRelativeAccuracy(newValue);
+ solver.resetRelativeAccuracy();
+ assertEquals(oldValue, solver.getRelativeAccuracy(), 1.0e-2);
+ }
+
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/BrentSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/BrentSolverTest.java
new file mode 100644
index 0000000..1690987
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/BrentSolverTest.java
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.MonitoredFunction;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Testcase for UnivariateRealSolver.
+ * Because Brent-Dekker is guaranteed to converge in less than the default
+ * maximum iteration count due to bisection fallback, it is quite hard to
+ * debug. I include measured iteration counts plus one in order to detect
+ * regressions. On average Brent-Dekker should use 4..5 iterations for the
+ * default absolute accuracy of 10E-8 for sinus and the quintic function around
+ * zero, and 5..10 iterations for the other zeros.
+ *
+ * @version $Revision:670469 $ $Date:2008-06-23 10:01:38 +0200 (lun., 23 juin 2008) $
+ */
+public final class BrentSolverTest extends TestCase {
+
+ public BrentSolverTest(String name) {
+ super(name);
+ }
+
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ // The sinus function is behaved well around the root at #pi. The second
+ // order derivative is zero, which means linar approximating methods will
+ // still converge quadratically.
+ UnivariateRealFunction f = new SinFunction();
+ double result;
+ UnivariateRealSolver solver = new BrentSolver(f);
+ // Somewhat benign interval. The function is monotone.
+ result = solver.solve(3, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 4 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 5);
+ // Larger and somewhat less benign interval. The function is grows first.
+ result = solver.solve(1, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ solver = new SecantSolver(f);
+ result = solver.solve(3, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 4 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 5);
+ result = solver.solve(1, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ assertEquals(result, solver.getResult(), 0);
+ }
+
+ public void testSinZero() throws MathException {
+ // The sinus function is behaved well around the root at #pi. The second
+ // order derivative is zero, which means linar approximating methods will
+ // still converge quadratically.
+ UnivariateRealFunction f = new SinFunction();
+ double result;
+ UnivariateRealSolver solver = new BrentSolver();
+ // Somewhat benign interval. The function is monotone.
+ result = solver.solve(f, 3, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 4 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 5);
+ // Larger and somewhat less benign interval. The function is grows first.
+ result = solver.solve(f, 1, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ solver = new SecantSolver();
+ result = solver.solve(f, 3, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 4 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 5);
+ result = solver.solve(f, 1, 4);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ assertEquals(result, solver.getResult(), 0);
+ }
+
+ public void testQuinticZero() throws MathException {
+ // The quintic function has zeros at 0, +-0.5 and +-1.
+ // Around the root of 0 the function is well behaved, with a second derivative
+ // of zero a 0.
+ // The other roots are less well to find, in particular the root at 1, because
+ // the function grows fast for x>1.
+ // The function has extrema (first derivative is zero) at 0.27195613 and 0.82221643,
+ // intervals containing these values are harder for the solvers.
+ UnivariateRealFunction f = new QuinticFunction();
+ double result;
+ // Brent-Dekker solver.
+ UnivariateRealSolver solver = new BrentSolver();
+ // Symmetric bracket around 0. Test whether solvers can handle hitting
+ // the root in the first iteration.
+ result = solver.solve(f, -0.2, 0.2);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ assertTrue(solver.getIterationCount() <= 2);
+ // 1 iterations on i586 JDK 1.4.1.
+ // Asymmetric bracket around 0, just for fun. Contains extremum.
+ result = solver.solve(f, -0.1, 0.3);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ // Large bracket around 0. Contains two extrema.
+ result = solver.solve(f, -0.3, 0.45);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ // 6 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 7);
+ // Benign bracket around 0.5, function is monotonous.
+ result = solver.solve(f, 0.3, 0.7);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 6 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 7);
+ // Less benign bracket around 0.5, contains one extremum.
+ result = solver.solve(f, 0.2, 0.6);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 6 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 7);
+ // Large, less benign bracket around 0.5, contains both extrema.
+ result = solver.solve(f, 0.05, 0.95);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 8 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 9);
+ // Relatively benign bracket around 1, function is monotonous. Fast growth for x>1
+ // is still a problem.
+ result = solver.solve(f, 0.85, 1.25);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 8 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 9);
+ // Less benign bracket around 1 with extremum.
+ result = solver.solve(f, 0.8, 1.2);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 8 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 9);
+ // Large bracket around 1. Monotonous.
+ result = solver.solve(f, 0.85, 1.75);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 10 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 11);
+ // Large bracket around 1. Interval contains extremum.
+ result = solver.solve(f, 0.55, 1.45);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 7 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 8);
+ // Very large bracket around 1 for testing fast growth behaviour.
+ result = solver.solve(f, 0.85, 5);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 12 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 13);
+ // Secant solver.
+ solver = new SecantSolver();
+ result = solver.solve(f, -0.2, 0.2);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ // 1 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 2);
+ result = solver.solve(f, -0.1, 0.3);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ // 5 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 6);
+ result = solver.solve(f, -0.3, 0.45);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ // 6 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 7);
+ result = solver.solve(f, 0.3, 0.7);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 7 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 8);
+ result = solver.solve(f, 0.2, 0.6);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 6 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 7);
+ result = solver.solve(f, 0.05, 0.95);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+ // 8 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 9);
+ result = solver.solve(f, 0.85, 1.25);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 10 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 11);
+ result = solver.solve(f, 0.8, 1.2);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 8 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 9);
+ result = solver.solve(f, 0.85, 1.75);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 14 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 15);
+ // The followig is especially slow because the solver first has to reduce
+ // the bracket to exclude the extremum. After that, convergence is rapide.
+ result = solver.solve(f, 0.55, 1.45);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 7 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 8);
+ result = solver.solve(f, 0.85, 5);
+ //System.out.println(
+ // "Root: " + result + " Iterations: " + solver.getIterationCount());
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ // 14 iterations on i586 JDK 1.4.1.
+ assertTrue(solver.getIterationCount() <= 15);
+ // Static solve method
+ result = UnivariateRealSolverUtils.solve(f, -0.2, 0.2);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+ result = UnivariateRealSolverUtils.solve(f, -0.1, 0.3);
+ assertEquals(result, 0, 1E-8);
+ result = UnivariateRealSolverUtils.solve(f, -0.3, 0.45);
+ assertEquals(result, 0, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.3, 0.7);
+ assertEquals(result, 0.5, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.2, 0.6);
+ assertEquals(result, 0.5, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.05, 0.95);
+ assertEquals(result, 0.5, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.85, 1.25);
+ assertEquals(result, 1.0, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.8, 1.2);
+ assertEquals(result, 1.0, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.85, 1.75);
+ assertEquals(result, 1.0, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.55, 1.45);
+ assertEquals(result, 1.0, 1E-6);
+ result = UnivariateRealSolverUtils.solve(f, 0.85, 5);
+ assertEquals(result, 1.0, 1E-6);
+ }
+
+ public void testRootEndpoints() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new BrentSolver();
+
+ // endpoint is root
+ double result = solver.solve(f, FastMath.PI, 4);
+ assertEquals(FastMath.PI, result, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 3, FastMath.PI);
+ assertEquals(FastMath.PI, result, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, FastMath.PI, 4, 3.5);
+ assertEquals(FastMath.PI, result, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 3, FastMath.PI, 3.07);
+ assertEquals(FastMath.PI, result, solver.getAbsoluteAccuracy());
+
+ }
+
+ public void testBadEndpoints() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new BrentSolver();
+ try { // bad interval
+ solver.solve(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try { // no bracket
+ solver.solve(f, 1, 1.5);
+ fail("Expecting IllegalArgumentException - non-bracketing");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try { // no bracket
+ solver.solve(f, 1, 1.5, 1.2);
+ fail("Expecting IllegalArgumentException - non-bracketing");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testInitialGuess() throws MathException {
+
+ MonitoredFunction f = new MonitoredFunction(new QuinticFunction());
+ UnivariateRealSolver solver = new BrentSolver();
+ double result;
+
+ // no guess
+ result = solver.solve(f, 0.6, 7.0);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ int referenceCallsCount = f.getCallsCount();
+ assertTrue(referenceCallsCount >= 13);
+
+ // invalid guess (it *is* a root, but outside of the range)
+ try {
+ result = solver.solve(f, 0.6, 7.0, 0.0);
+ fail("an IllegalArgumentException was expected");
+ } catch (IllegalArgumentException iae) {
+ // expected behaviour
+ }
+
+ // bad guess
+ f.setCallsCount(0);
+ result = solver.solve(f, 0.6, 7.0, 0.61);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ assertTrue(f.getCallsCount() > referenceCallsCount);
+
+ // good guess
+ f.setCallsCount(0);
+ result = solver.solve(f, 0.6, 7.0, 0.999999);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ assertTrue(f.getCallsCount() < referenceCallsCount);
+
+ // perfect guess
+ f.setCallsCount(0);
+ result = solver.solve(f, 0.6, 7.0, 1.0);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ assertEquals(0, solver.getIterationCount());
+ assertEquals(1, f.getCallsCount());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/LaguerreSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/LaguerreSolverTest.java
new file mode 100644
index 0000000..53584b5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/LaguerreSolverTest.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.complex.Complex;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Laguerre solver.
+ * <p>
+ * Laguerre's method is very efficient in solving polynomials. Test runs
+ * show that for a default absolute accuracy of 1E-6, it generally takes
+ * less than 5 iterations to find one root, provided solveAll() is not
+ * invoked, and 15 to 20 iterations to find all roots for quintic function.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class LaguerreSolverTest extends TestCase {
+
+ /**
+ * Test deprecated APIs.
+ */
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ double min, max, expected, result, tolerance;
+
+ // p(x) = 4x - 1
+ double coefficients[] = { -1.0, 4.0 };
+ PolynomialFunction f = new PolynomialFunction(coefficients);
+ UnivariateRealSolver solver = new LaguerreSolver(f);
+
+ min = 0.0; max = 1.0; expected = 0.25;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the linear function.
+ */
+ public void testLinearFunction() throws MathException {
+ double min, max, expected, result, tolerance;
+
+ // p(x) = 4x - 1
+ double coefficients[] = { -1.0, 4.0 };
+ PolynomialFunction f = new PolynomialFunction(coefficients);
+ UnivariateRealSolver solver = new LaguerreSolver();
+
+ min = 0.0; max = 1.0; expected = 0.25;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quadratic function.
+ */
+ public void testQuadraticFunction() throws MathException {
+ double min, max, expected, result, tolerance;
+
+ // p(x) = 2x^2 + 5x - 3 = (x+3)(2x-1)
+ double coefficients[] = { -3.0, 5.0, 2.0 };
+ PolynomialFunction f = new PolynomialFunction(coefficients);
+ UnivariateRealSolver solver = new LaguerreSolver();
+
+ min = 0.0; max = 2.0; expected = 0.5;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -4.0; max = -1.0; expected = -3.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ double min, max, expected, result, tolerance;
+
+ // p(x) = x^5 - x^4 - 12x^3 + x^2 - x - 12 = (x+1)(x+3)(x-4)(x^2-x+1)
+ double coefficients[] = { -12.0, -1.0, 1.0, -12.0, -1.0, 1.0 };
+ PolynomialFunction f = new PolynomialFunction(coefficients);
+ UnivariateRealSolver solver = new LaguerreSolver();
+
+ min = -2.0; max = 2.0; expected = -1.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -5.0; max = -2.5; expected = -3.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 3.0; max = 6.0; expected = 4.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quintic function using solveAll().
+ */
+ public void testQuinticFunction2() throws MathException {
+ double initial = 0.0, tolerance;
+ Complex expected, result[];
+
+ // p(x) = x^5 + 4x^3 + x^2 + 4 = (x+1)(x^2-x+1)(x^2+4)
+ double coefficients[] = { 4.0, 0.0, 1.0, 4.0, 0.0, 1.0 };
+ LaguerreSolver solver = new LaguerreSolver();
+ result = solver.solveAll(coefficients, initial);
+
+ expected = new Complex(0.0, -2.0);
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected.abs() * solver.getRelativeAccuracy()));
+ TestUtils.assertContains(result, expected, tolerance);
+
+ expected = new Complex(0.0, 2.0);
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected.abs() * solver.getRelativeAccuracy()));
+ TestUtils.assertContains(result, expected, tolerance);
+
+ expected = new Complex(0.5, 0.5 * FastMath.sqrt(3.0));
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected.abs() * solver.getRelativeAccuracy()));
+ TestUtils.assertContains(result, expected, tolerance);
+
+ expected = new Complex(-1.0, 0.0);
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected.abs() * solver.getRelativeAccuracy()));
+ TestUtils.assertContains(result, expected, tolerance);
+
+ expected = new Complex(0.5, -0.5 * FastMath.sqrt(3.0));
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected.abs() * solver.getRelativeAccuracy()));
+ TestUtils.assertContains(result, expected, tolerance);
+ }
+
+ /**
+ * Test of parameters for the solver.
+ */
+ public void testParameters() throws Exception {
+ double coefficients[] = { -3.0, 5.0, 2.0 };
+ PolynomialFunction f = new PolynomialFunction(coefficients);
+ UnivariateRealSolver solver = new LaguerreSolver();
+
+ try {
+ // bad interval
+ solver.solve(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // no bracketing
+ solver.solve(f, 2, 3);
+ fail("Expecting IllegalArgumentException - no bracketing");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad function
+ solver.solve(new SinFunction(), -1, 1);
+ fail("Expecting IllegalArgumentException - bad function");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/MullerSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/MullerSolverTest.java
new file mode 100644
index 0000000..b8ae755
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/MullerSolverTest.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.analysis.solvers;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.Expm1Function;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Muller solver.
+ * <p>
+ * Muller's method converges almost quadratically near roots, but it can
+ * be very slow in regions far away from zeros. Test runs show that for
+ * reasonably good initial values, for a default absolute accuracy of 1E-6,
+ * it generally takes 5 to 10 iterations for the solver to converge.
+ * <p>
+ * Tests for the exponential function illustrate the situations where
+ * Muller solver performs poorly.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class MullerSolverTest extends TestCase {
+
+ /**
+ * Test deprecated APIs.
+ */
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new MullerSolver(f);
+ double min, max, expected, result, tolerance;
+
+ min = 3.0; max = 4.0; expected = FastMath.PI;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1.0; max = 1.5; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test deprecated APIs.
+ */
+ @Deprecated
+ public void testDeprecated2() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ MullerSolver solver = new MullerSolver(f);
+ double min, max, expected, result, tolerance;
+
+ min = -0.4; max = 0.2; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0.75; max = 1.5; expected = 1.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -0.9; max = -0.2; expected = -0.5;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the sine function.
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = 3.0; max = 4.0; expected = FastMath.PI;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1.0; max = 1.5; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the sine function using solve2().
+ */
+ public void testSinFunction2() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ MullerSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = 3.0; max = 4.0; expected = FastMath.PI;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1.0; max = 1.5; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -0.4; max = 0.2; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0.75; max = 1.5; expected = 1.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -0.9; max = -0.2; expected = -0.5;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quintic function using solve2().
+ */
+ public void testQuinticFunction2() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ MullerSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -0.4; max = 0.2; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0.75; max = 1.5; expected = 1.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -0.9; max = -0.2; expected = -0.5;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the exponential function.
+ * <p>
+ * It takes 10 to 15 iterations for the last two tests to converge.
+ * In fact, if not for the bisection alternative, the solver would
+ * exceed the default maximal iteration of 100.
+ */
+ public void testExpm1Function() throws MathException {
+ UnivariateRealFunction f = new Expm1Function();
+ UnivariateRealSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -1.0; max = 2.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -20.0; max = 10.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -50.0; max = 100.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the exponential function using solve2().
+ * <p>
+ * It takes 25 to 50 iterations for the last two tests to converge.
+ */
+ public void testExpm1Function2() throws MathException {
+ UnivariateRealFunction f = new Expm1Function();
+ MullerSolver solver = new MullerSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -1.0; max = 2.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -20.0; max = 10.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -50.0; max = 100.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve2(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the solver.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new MullerSolver();
+
+ try {
+ // bad interval
+ solver.solve(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // no bracketing
+ solver.solve(f, 2, 3);
+ fail("Expecting IllegalArgumentException - no bracketing");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/NewtonSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/NewtonSolverTest.java
new file mode 100644
index 0000000..e7966fe
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/NewtonSolverTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.analysis.solvers;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.DifferentiableUnivariateRealFunction;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.util.FastMath;
+
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class NewtonSolverTest extends TestCase {
+
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ DifferentiableUnivariateRealFunction f = new SinFunction();
+ double result;
+
+ UnivariateRealSolver solver = new NewtonSolver(f);
+ result = solver.solve(3, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(1, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ assertEquals(result, solver.getResult(), 0);
+ assertTrue(solver.getIterationCount() > 0);
+ }
+
+ /**
+ *
+ */
+ public void testSinZero() throws MathException {
+ DifferentiableUnivariateRealFunction f = new SinFunction();
+ double result;
+
+ UnivariateRealSolver solver = new NewtonSolver();
+ result = solver.solve(f, 3, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 1, 4);
+ assertEquals(result, FastMath.PI, solver.getAbsoluteAccuracy());
+
+ assertEquals(result, solver.getResult(), 0);
+ assertTrue(solver.getIterationCount() > 0);
+ }
+
+ /**
+ *
+ */
+ public void testQuinticZero() throws MathException {
+ DifferentiableUnivariateRealFunction f = new QuinticFunction();
+ double result;
+
+ UnivariateRealSolver solver = new NewtonSolver();
+ result = solver.solve(f, -0.2, 0.2);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, -0.1, 0.3);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, -0.3, 0.45);
+ assertEquals(result, 0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.3, 0.7);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.2, 0.6);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.05, 0.95);
+ assertEquals(result, 0.5, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 1.25);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.8, 1.2);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 1.75);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.55, 1.45);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+
+ result = solver.solve(f, 0.85, 5);
+ assertEquals(result, 1.0, solver.getAbsoluteAccuracy());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/RiddersSolverTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/RiddersSolverTest.java
new file mode 100644
index 0000000..c6d17ef
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/RiddersSolverTest.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.analysis.solvers;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.Expm1Function;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for Ridders solver.
+ * <p>
+ * Ridders' method converges superlinearly, more specific, its rate of
+ * convergence is sqrt(2). Test runs show that for a default absolute
+ * accuracy of 1E-6, it generally takes less than 5 iterations for close
+ * initial bracket and 5 to 10 iterations for distant initial bracket
+ * to converge.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public final class RiddersSolverTest extends TestCase {
+
+ /**
+ * Test the deprecated APIs.
+ */
+ @Deprecated
+ public void testDeprecated() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new RiddersSolver(f);
+ double min, max, expected, result, tolerance;
+
+ min = 3.0; max = 4.0; expected = FastMath.PI;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1.0; max = 1.5; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the sine function.
+ */
+ public void testSinFunction() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new RiddersSolver();
+ double min, max, expected, result, tolerance;
+
+ min = 3.0; max = 4.0; expected = FastMath.PI;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -1.0; max = 1.5; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the quintic function.
+ */
+ public void testQuinticFunction() throws MathException {
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealSolver solver = new RiddersSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -0.4; max = 0.2; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = 0.75; max = 1.5; expected = 1.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -0.9; max = -0.2; expected = -0.5;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of solver for the exponential function.
+ */
+ public void testExpm1Function() throws MathException {
+ UnivariateRealFunction f = new Expm1Function();
+ UnivariateRealSolver solver = new RiddersSolver();
+ double min, max, expected, result, tolerance;
+
+ min = -1.0; max = 2.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -20.0; max = 10.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+
+ min = -50.0; max = 100.0; expected = 0.0;
+ tolerance = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(expected * solver.getRelativeAccuracy()));
+ result = solver.solve(f, min, max);
+ assertEquals(expected, result, tolerance);
+ }
+
+ /**
+ * Test of parameters for the solver.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealSolver solver = new RiddersSolver();
+
+ try {
+ // bad interval
+ solver.solve(f, 1, -1);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // no bracketing
+ solver.solve(f, 2, 3);
+ fail("Expecting IllegalArgumentException - no bracketing");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImplTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImplTest.java
new file mode 100644
index 0000000..ad786d1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverFactoryImplTest.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.solvers;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class UnivariateRealSolverFactoryImplTest extends TestCase {
+
+ /** solver factory */
+ private UnivariateRealSolverFactory factory;
+
+ /**
+ * @throws java.lang.Exception
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ factory = new UnivariateRealSolverFactoryImpl();
+ }
+
+ /**
+ * @throws java.lang.Exception
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ factory = null;
+ super.tearDown();
+ }
+
+ public void testNewBisectionSolverValid() {
+ UnivariateRealSolver solver = factory.newBisectionSolver();
+ assertNotNull(solver);
+ assertTrue(solver instanceof BisectionSolver);
+ }
+
+ public void testNewNewtonSolverValid() {
+ UnivariateRealSolver solver = factory.newNewtonSolver();
+ assertNotNull(solver);
+ assertTrue(solver instanceof NewtonSolver);
+ }
+
+ public void testNewBrentSolverValid() {
+ UnivariateRealSolver solver = factory.newBrentSolver();
+ assertNotNull(solver);
+ assertTrue(solver instanceof BrentSolver);
+ }
+
+ public void testNewSecantSolverValid() {
+ UnivariateRealSolver solver = factory.newSecantSolver();
+ assertNotNull(solver);
+ assertTrue(solver instanceof SecantSolver);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtilsTest.java b/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtilsTest.java
new file mode 100644
index 0000000..7bab7cf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/analysis/solvers/UnivariateRealSolverUtilsTest.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.analysis.solvers;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * @version $Revision: 1003901 $ $Date: 2010-10-03 00:11:04 +0200 (dim. 03 oct. 2010) $
+ */
+public class UnivariateRealSolverUtilsTest extends TestCase {
+
+ protected UnivariateRealFunction sin = new SinFunction();
+
+ public void testSolveNull() throws MathException {
+ try {
+ UnivariateRealSolverUtils.solve(null, 0.0, 4.0);
+ fail();
+ } catch(IllegalArgumentException ex){
+ // success
+ }
+ }
+
+ public void testSolveBadEndpoints() throws MathException {
+ try { // bad endpoints
+ UnivariateRealSolverUtils.solve(sin, -0.1, 4.0, 4.0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testSolveBadAccuracy() throws MathException {
+ try { // bad accuracy
+ UnivariateRealSolverUtils.solve(sin, 0.0, 4.0, 0.0);
+// fail("Expecting IllegalArgumentException"); // TODO needs rework since convergence behaviour was changed
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testSolveSin() throws MathException {
+ double x = UnivariateRealSolverUtils.solve(sin, 1.0, 4.0);
+ assertEquals(FastMath.PI, x, 1.0e-4);
+ }
+
+ public void testSolveAccuracyNull() throws MathException {
+ try {
+ double accuracy = 1.0e-6;
+ UnivariateRealSolverUtils.solve(null, 0.0, 4.0, accuracy);
+ fail();
+ } catch(IllegalArgumentException ex){
+ // success
+ }
+ }
+
+ public void testSolveAccuracySin() throws MathException {
+ double accuracy = 1.0e-6;
+ double x = UnivariateRealSolverUtils.solve(sin, 1.0,
+ 4.0, accuracy);
+ assertEquals(FastMath.PI, x, accuracy);
+ }
+
+ public void testSolveNoRoot() throws MathException {
+ try {
+ UnivariateRealSolverUtils.solve(sin, 1.0, 1.5);
+ fail("Expecting IllegalArgumentException ");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBracketSin() throws MathException {
+ double[] result = UnivariateRealSolverUtils.bracket(sin,
+ 0.0, -2.0, 2.0);
+ assertTrue(sin.value(result[0]) < 0);
+ assertTrue(sin.value(result[1]) > 0);
+ }
+
+ public void testBracketEndpointRoot() throws MathException {
+ double[] result = UnivariateRealSolverUtils.bracket(sin, 1.5, 0, 2.0);
+ assertEquals(0.0, sin.value(result[0]), 1.0e-15);
+ assertTrue(sin.value(result[1]) > 0);
+ }
+
+ public void testNullFunction() throws MathException {
+ try { // null function
+ UnivariateRealSolverUtils.bracket(null, 1.5, 0, 2.0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBadInitial() throws MathException {
+ try { // initial not between endpoints
+ UnivariateRealSolverUtils.bracket(sin, 2.5, 0, 2.0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBadEndpoints() throws MathException {
+ try { // endpoints not valid
+ UnivariateRealSolverUtils.bracket(sin, 1.5, 2.0, 1.0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBadMaximumIterations() throws MathException {
+ try { // bad maximum iterations
+ UnivariateRealSolverUtils.bracket(sin, 1.5, 0, 2.0, 0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/complex/ComplexFieldTest.java b/src/test/java/org/apache/commons/math/complex/ComplexFieldTest.java
new file mode 100644
index 0000000..dc6c0f3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/ComplexFieldTest.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.complex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.TestUtils;
+import org.junit.Test;
+
+public class ComplexFieldTest {
+
+ @Test
+ public void testZero() {
+ assertEquals(Complex.ZERO, ComplexField.getInstance().getZero());
+ }
+
+ @Test
+ public void testOne() {
+ assertEquals(Complex.ONE, ComplexField.getInstance().getOne());
+ }
+
+ @Test
+ public void testSerial() {
+ // deserializing the singleton should give the singleton itself back
+ ComplexField field = ComplexField.getInstance();
+ assertTrue(field == TestUtils.serializeAndRecover(field));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/complex/ComplexFormatAbstractTest.java b/src/test/java/org/apache/commons/math/complex/ComplexFormatAbstractTest.java
new file mode 100644
index 0000000..b09a045
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/ComplexFormatAbstractTest.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.complex;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math.util.CompositeFormat;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public abstract class ComplexFormatAbstractTest extends TestCase {
+
+ CompositeFormat complexFormat = null;
+ ComplexFormat complexFormatJ = null;
+
+ protected abstract Locale getLocale();
+
+ protected abstract char getDecimalCharacter();
+
+ @Override
+ protected void setUp() throws Exception {
+ complexFormat = ComplexFormat.getInstance(getLocale());
+ complexFormatJ = ComplexFormat.getInstance(getLocale());
+ complexFormatJ.setImaginaryCharacter("j");
+ }
+
+ public void testSimpleNoDecimals() {
+ Complex c = new Complex(1, 1);
+ String expected = "1 + 1i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimals() {
+ Complex c = new Complex(1.23, 1.43);
+ String expected = "1" + getDecimalCharacter() + "23 + 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimalsTrunc() {
+ Complex c = new Complex(1.2323, 1.4343);
+ String expected = "1" + getDecimalCharacter() + "23 + 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeReal() {
+ Complex c = new Complex(-1.2323, 1.4343);
+ String expected = "-1" + getDecimalCharacter() + "23 + 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeImaginary() {
+ Complex c = new Complex(1.2323, -1.4343);
+ String expected = "1" + getDecimalCharacter() + "23 - 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeBoth() {
+ Complex c = new Complex(-1.2323, -1.4343);
+ String expected = "-1" + getDecimalCharacter() + "23 - 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testZeroReal() {
+ Complex c = new Complex(0.0, -1.4343);
+ String expected = "0 - 1" + getDecimalCharacter() + "43i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testZeroImaginary() {
+ Complex c = new Complex(30.233, 0);
+ String expected = "30" + getDecimalCharacter() + "23";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testDifferentImaginaryChar() {
+ Complex c = new Complex(1, 1);
+ String expected = "1 + 1j";
+ String actual = complexFormatJ.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testStaticFormatComplex() {
+ Locale defaultLocal = Locale.getDefault();
+ Locale.setDefault(getLocale());
+
+ Complex c = new Complex(232.222, -342.33);
+ String expected = "232" + getDecimalCharacter() + "22 - 342" + getDecimalCharacter() + "33i";
+ String actual = ComplexFormat.formatComplex(c);
+ assertEquals(expected, actual);
+
+ Locale.setDefault(defaultLocal);
+ }
+
+ public void testNan() {
+ Complex c = new Complex(Double.NaN, Double.NaN);
+ String expected = "(NaN) + (NaN)i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testPositiveInfinity() {
+ Complex c = new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ String expected = "(Infinity) + (Infinity)i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeInfinity() {
+ Complex c = new Complex(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+ String expected = "(-Infinity) - (Infinity)i";
+ String actual = complexFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testParseSimpleNoDecimals() {
+ String source = "1 + 1i";
+ Complex expected = new Complex(1, 1);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseSimpleWithDecimals() {
+ String source = "1" + getDecimalCharacter() + "23 + 1" + getDecimalCharacter() + "43i";
+ Complex expected = new Complex(1.23, 1.43);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseSimpleWithDecimalsTrunc() {
+ String source = "1" + getDecimalCharacter() + "2323 + 1" + getDecimalCharacter() + "4343i";
+ Complex expected = new Complex(1.2323, 1.4343);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeReal() {
+ String source = "-1" + getDecimalCharacter() + "2323 + 1" + getDecimalCharacter() + "4343i";
+ Complex expected = new Complex(-1.2323, 1.4343);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeImaginary() {
+ String source = "1" + getDecimalCharacter() + "2323 - 1" + getDecimalCharacter() + "4343i";
+ Complex expected = new Complex(1.2323, -1.4343);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeBoth() {
+ String source = "-1" + getDecimalCharacter() + "2323 - 1" + getDecimalCharacter() + "4343i";
+ Complex expected = new Complex(-1.2323, -1.4343);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseZeroReal() {
+ String source = "0" + getDecimalCharacter() + "0 - 1" + getDecimalCharacter() + "4343i";
+ Complex expected = new Complex(0.0, -1.4343);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseZeroImaginary() {
+ String source = "-1" + getDecimalCharacter() + "2323";
+ Complex expected = new Complex(-1.2323, 0);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseDifferentImaginaryChar() {
+ String source = "-1" + getDecimalCharacter() + "2323 - 1" + getDecimalCharacter() + "4343j";
+ Complex expected = new Complex(-1.2323, -1.4343);
+ try {
+ Complex actual = (Complex)complexFormatJ.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNan() {
+ String source = "(NaN) + (NaN)i";
+ Complex expected = new Complex(Double.NaN, Double.NaN);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParsePositiveInfinity() {
+ String source = "(Infinity) + (Infinity)i";
+ Complex expected = new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testPaseNegativeInfinity() {
+ String source = "(-Infinity) - (Infinity)i";
+ Complex expected = new Complex(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+ try {
+ Complex actual = (Complex)complexFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testConstructorSingleFormat() {
+ NumberFormat nf = NumberFormat.getInstance();
+ ComplexFormat cf = new ComplexFormat(nf);
+ assertNotNull(cf);
+ assertEquals(nf, cf.getRealFormat());
+ }
+
+ public void testGetImaginaryFormat() {
+ NumberFormat nf = NumberFormat.getInstance();
+ ComplexFormat cf = new ComplexFormat();
+
+ assertNotSame(nf, cf.getImaginaryFormat());
+ cf.setImaginaryFormat(nf);
+ assertSame(nf, cf.getImaginaryFormat());
+ }
+
+ public void testSetImaginaryFormatNull() {
+ try {
+ ComplexFormat cf = new ComplexFormat();
+ cf.setImaginaryFormat(null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testSetRealFormatNull() {
+ try {
+ ComplexFormat cf = new ComplexFormat();
+ cf.setRealFormat(null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testGetRealFormat() {
+ NumberFormat nf = NumberFormat.getInstance();
+ ComplexFormat cf = new ComplexFormat();
+
+ assertNotSame(nf, cf.getRealFormat());
+ cf.setRealFormat(nf);
+ assertSame(nf, cf.getRealFormat());
+ }
+
+ public void testSetImaginaryCharacterNull() {
+ try {
+ ComplexFormat cf = new ComplexFormat();
+ cf.setImaginaryCharacter(null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testSetImaginaryCharacterEmpty() {
+ try {
+ ComplexFormat cf = new ComplexFormat();
+ cf.setImaginaryCharacter("");
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testFormatNumber() {
+ CompositeFormat cf = ComplexFormat.getInstance(getLocale());
+ Double pi = Double.valueOf(FastMath.PI);
+ String text = cf.format(pi);
+ assertEquals("3" + getDecimalCharacter() + "14", text);
+ }
+
+ public void testFormatObject() {
+ try {
+ CompositeFormat cf = new ComplexFormat();
+ Object object = new Object();
+ cf.format(object);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testForgottenImaginaryCharacter() {
+ ParsePosition pos = new ParsePosition(0);
+ assertNull(new ComplexFormat().parse("1 + 1", pos));
+ assertEquals(5, pos.getErrorIndex());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/complex/ComplexFormatTest.java b/src/test/java/org/apache/commons/math/complex/ComplexFormatTest.java
new file mode 100644
index 0000000..5ae9299
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/ComplexFormatTest.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.complex;
+
+import java.util.Locale;
+
+
+public class ComplexFormatTest extends ComplexFormatAbstractTest {
+ @Override
+ protected char getDecimalCharacter() {
+ return '.';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/complex/ComplexTest.java b/src/test/java/org/apache/commons/math/complex/ComplexTest.java
new file mode 100644
index 0000000..2564195
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/ComplexTest.java
@@ -0,0 +1,996 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class ComplexTest extends TestCase {
+
+
+ private double inf = Double.POSITIVE_INFINITY;
+ private double neginf = Double.NEGATIVE_INFINITY;
+ private double nan = Double.NaN;
+ private double pi = FastMath.PI;
+ private Complex oneInf = new Complex(1, inf);
+ private Complex oneNegInf = new Complex(1, neginf);
+ private Complex infOne = new Complex(inf, 1);
+ private Complex infZero = new Complex(inf, 0);
+ private Complex infNaN = new Complex(inf, nan);
+ private Complex infNegInf = new Complex(inf, neginf);
+ private Complex infInf = new Complex(inf, inf);
+ private Complex negInfInf = new Complex(neginf, inf);
+ private Complex negInfZero = new Complex(neginf, 0);
+ private Complex negInfOne = new Complex(neginf, 1);
+ private Complex negInfNaN = new Complex(neginf, nan);
+ private Complex negInfNegInf = new Complex(neginf, neginf);
+ private Complex oneNaN = new Complex(1, nan);
+ private Complex zeroInf = new Complex(0, inf);
+ private Complex zeroNaN = new Complex(0, nan);
+ private Complex nanInf = new Complex(nan, inf);
+ private Complex nanNegInf = new Complex(nan, neginf);
+ private Complex nanZero = new Complex(nan, 0);
+
+ public void testConstructor() {
+ Complex z = new Complex(3.0, 4.0);
+ assertEquals(3.0, z.getReal(), 1.0e-5);
+ assertEquals(4.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testConstructorNaN() {
+ Complex z = new Complex(3.0, Double.NaN);
+ assertTrue(z.isNaN());
+
+ z = new Complex(nan, 4.0);
+ assertTrue(z.isNaN());
+
+ z = new Complex(3.0, 4.0);
+ assertFalse(z.isNaN());
+ }
+
+ public void testAbs() {
+ Complex z = new Complex(3.0, 4.0);
+ assertEquals(5.0, z.abs(), 1.0e-5);
+ }
+
+ public void testAbsNaN() {
+ assertTrue(Double.isNaN(Complex.NaN.abs()));
+ Complex z = new Complex(inf, nan);
+ assertTrue(Double.isNaN(z.abs()));
+ }
+
+ public void testAbsInfinite() {
+ Complex z = new Complex(inf, 0);
+ assertEquals(inf, z.abs(), 0);
+ z = new Complex(0, neginf);
+ assertEquals(inf, z.abs(), 0);
+ z = new Complex(inf, neginf);
+ assertEquals(inf, z.abs(), 0);
+ }
+
+ public void testAdd() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex y = new Complex(5.0, 6.0);
+ Complex z = x.add(y);
+ assertEquals(8.0, z.getReal(), 1.0e-5);
+ assertEquals(10.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testAddNaN() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.add(Complex.NaN);
+ assertTrue(z.isNaN());
+ z = new Complex(1, nan);
+ Complex w = x.add(z);
+ assertEquals(w.getReal(), 4.0, 0);
+ assertTrue(Double.isNaN(w.getImaginary()));
+ }
+
+ public void testAddInfinite() {
+ Complex x = new Complex(1, 1);
+ Complex z = new Complex(inf, 0);
+ Complex w = x.add(z);
+ assertEquals(w.getImaginary(), 1, 0);
+ assertEquals(inf, w.getReal(), 0);
+
+ x = new Complex(neginf, 0);
+ assertTrue(Double.isNaN(x.add(z).getReal()));
+ }
+
+ public void testConjugate() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.conjugate();
+ assertEquals(3.0, z.getReal(), 1.0e-5);
+ assertEquals(-4.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testConjugateNaN() {
+ Complex z = Complex.NaN.conjugate();
+ assertTrue(z.isNaN());
+ }
+
+ public void testConjugateInfiinite() {
+ Complex z = new Complex(0, inf);
+ assertEquals(neginf, z.conjugate().getImaginary(), 0);
+ z = new Complex(0, neginf);
+ assertEquals(inf, z.conjugate().getImaginary(), 0);
+ }
+
+ public void testDivide() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex y = new Complex(5.0, 6.0);
+ Complex z = x.divide(y);
+ assertEquals(39.0 / 61.0, z.getReal(), 1.0e-5);
+ assertEquals(2.0 / 61.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testDivideReal() {
+ Complex x = new Complex(2d, 3d);
+ Complex y = new Complex(2d, 0d);
+ assertEquals(new Complex(1d, 1.5), x.divide(y));
+
+ }
+
+ public void testDivideImaginary() {
+ Complex x = new Complex(2d, 3d);
+ Complex y = new Complex(0d, 2d);
+ assertEquals(new Complex(1.5d, -1d), x.divide(y));
+ }
+
+ public void testDivideInfinite() {
+ Complex x = new Complex(3, 4);
+ Complex w = new Complex(neginf, inf);
+ assertTrue(x.divide(w).equals(Complex.ZERO));
+
+ Complex z = w.divide(x);
+ assertTrue(Double.isNaN(z.getReal()));
+ assertEquals(inf, z.getImaginary(), 0);
+
+ w = new Complex(inf, inf);
+ z = w.divide(x);
+ assertTrue(Double.isNaN(z.getImaginary()));
+ assertEquals(inf, z.getReal(), 0);
+
+ w = new Complex(1, inf);
+ z = w.divide(w);
+ assertTrue(Double.isNaN(z.getReal()));
+ assertTrue(Double.isNaN(z.getImaginary()));
+ }
+
+ public void testDivideZero() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.divide(Complex.ZERO);
+ assertEquals(z, Complex.NaN);
+ }
+
+ public void testDivideNaN() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.divide(Complex.NaN);
+ assertTrue(z.isNaN());
+ }
+
+ public void testDivideNaNInf() {
+ Complex z = oneInf.divide(Complex.ONE);
+ assertTrue(Double.isNaN(z.getReal()));
+ assertEquals(inf, z.getImaginary(), 0);
+
+ z = negInfNegInf.divide(oneNaN);
+ assertTrue(Double.isNaN(z.getReal()));
+ assertTrue(Double.isNaN(z.getImaginary()));
+
+ z = negInfInf.divide(Complex.ONE);
+ assertTrue(Double.isNaN(z.getReal()));
+ assertTrue(Double.isNaN(z.getImaginary()));
+ }
+
+ public void testMultiply() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex y = new Complex(5.0, 6.0);
+ Complex z = x.multiply(y);
+ assertEquals(-9.0, z.getReal(), 1.0e-5);
+ assertEquals(38.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testMultiplyNaN() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.multiply(Complex.NaN);
+ assertTrue(z.isNaN());
+ }
+
+ public void testMultiplyNaNInf() {
+ Complex z = new Complex(1,1);
+ Complex w = z.multiply(infOne);
+ assertEquals(w.getReal(), inf, 0);
+ assertEquals(w.getImaginary(), inf, 0);
+
+ // [MATH-164]
+ assertTrue(new Complex( 1,0).multiply(infInf).equals(Complex.INF));
+ assertTrue(new Complex(-1,0).multiply(infInf).equals(Complex.INF));
+ assertTrue(new Complex( 1,0).multiply(negInfZero).equals(Complex.INF));
+
+ w = oneInf.multiply(oneNegInf);
+ assertEquals(w.getReal(), inf, 0);
+ assertEquals(w.getImaginary(), inf, 0);
+
+ w = negInfNegInf.multiply(oneNaN);
+ assertTrue(Double.isNaN(w.getReal()));
+ assertTrue(Double.isNaN(w.getImaginary()));
+ }
+
+ public void testScalarMultiply() {
+ Complex x = new Complex(3.0, 4.0);
+ double y = 2.0;
+ Complex z = x.multiply(y);
+ assertEquals(6.0, z.getReal(), 1.0e-5);
+ assertEquals(8.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testScalarMultiplyNaN() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.multiply(Double.NaN);
+ assertTrue(z.isNaN());
+ }
+
+ public void testScalarMultiplyInf() {
+ Complex z = new Complex(1,1);
+ Complex w = z.multiply(Double.POSITIVE_INFINITY);
+ assertEquals(w.getReal(), inf, 0);
+ assertEquals(w.getImaginary(), inf, 0);
+
+ w = z.multiply(Double.NEGATIVE_INFINITY);
+ assertEquals(w.getReal(), inf, 0);
+ assertEquals(w.getImaginary(), inf, 0);
+ }
+
+ public void testNegate() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.negate();
+ assertEquals(-3.0, z.getReal(), 1.0e-5);
+ assertEquals(-4.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testNegateNaN() {
+ Complex z = Complex.NaN.negate();
+ assertTrue(z.isNaN());
+ }
+
+ public void testSubtract() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex y = new Complex(5.0, 6.0);
+ Complex z = x.subtract(y);
+ assertEquals(-2.0, z.getReal(), 1.0e-5);
+ assertEquals(-2.0, z.getImaginary(), 1.0e-5);
+ }
+
+ public void testSubtractNaN() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex z = x.subtract(Complex.NaN);
+ assertTrue(z.isNaN());
+ }
+
+ public void testEqualsNull() {
+ Complex x = new Complex(3.0, 4.0);
+ assertFalse(x.equals(null));
+ }
+
+ public void testEqualsClass() {
+ Complex x = new Complex(3.0, 4.0);
+ assertFalse(x.equals(this));
+ }
+
+ public void testEqualsSame() {
+ Complex x = new Complex(3.0, 4.0);
+ assertTrue(x.equals(x));
+ }
+
+ public void testEqualsTrue() {
+ Complex x = new Complex(3.0, 4.0);
+ Complex y = new Complex(3.0, 4.0);
+ assertTrue(x.equals(y));
+ }
+
+ public void testEqualsRealDifference() {
+ Complex x = new Complex(0.0, 0.0);
+ Complex y = new Complex(0.0 + Double.MIN_VALUE, 0.0);
+ assertFalse(x.equals(y));
+ }
+
+ public void testEqualsImaginaryDifference() {
+ Complex x = new Complex(0.0, 0.0);
+ Complex y = new Complex(0.0, 0.0 + Double.MIN_VALUE);
+ assertFalse(x.equals(y));
+ }
+
+ public void testEqualsNaN() {
+ Complex realNaN = new Complex(Double.NaN, 0.0);
+ Complex imaginaryNaN = new Complex(0.0, Double.NaN);
+ Complex complexNaN = Complex.NaN;
+ assertTrue(realNaN.equals(imaginaryNaN));
+ assertTrue(imaginaryNaN.equals(complexNaN));
+ assertTrue(realNaN.equals(complexNaN));
+ }
+
+ public void testHashCode() {
+ Complex x = new Complex(0.0, 0.0);
+ Complex y = new Complex(0.0, 0.0 + Double.MIN_VALUE);
+ assertFalse(x.hashCode()==y.hashCode());
+ y = new Complex(0.0 + Double.MIN_VALUE, 0.0);
+ assertFalse(x.hashCode()==y.hashCode());
+ Complex realNaN = new Complex(Double.NaN, 0.0);
+ Complex imaginaryNaN = new Complex(0.0, Double.NaN);
+ assertEquals(realNaN.hashCode(), imaginaryNaN.hashCode());
+ assertEquals(imaginaryNaN.hashCode(), Complex.NaN.hashCode());
+ }
+
+ public void testAcos() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(0.936812, -2.30551);
+ TestUtils.assertEquals(expected, z.acos(), 1.0e-5);
+ TestUtils.assertEquals(new Complex(FastMath.acos(0), 0),
+ Complex.ZERO.acos(), 1.0e-12);
+ }
+
+ public void testAcosInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.acos());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.acos());
+ TestUtils.assertSame(Complex.NaN, infOne.acos());
+ TestUtils.assertSame(Complex.NaN, negInfOne.acos());
+ TestUtils.assertSame(Complex.NaN, infInf.acos());
+ TestUtils.assertSame(Complex.NaN, infNegInf.acos());
+ TestUtils.assertSame(Complex.NaN, negInfInf.acos());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.acos());
+ }
+
+ public void testAcosNaN() {
+ assertTrue(Complex.NaN.acos().isNaN());
+ }
+
+ public void testAsin() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(0.633984, 2.30551);
+ TestUtils.assertEquals(expected, z.asin(), 1.0e-5);
+ }
+
+ public void testAsinNaN() {
+ assertTrue(Complex.NaN.asin().isNaN());
+ }
+
+ public void testAsinInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.asin());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.asin());
+ TestUtils.assertSame(Complex.NaN, infOne.asin());
+ TestUtils.assertSame(Complex.NaN, negInfOne.asin());
+ TestUtils.assertSame(Complex.NaN, infInf.asin());
+ TestUtils.assertSame(Complex.NaN, infNegInf.asin());
+ TestUtils.assertSame(Complex.NaN, negInfInf.asin());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.asin());
+ }
+
+
+ public void testAtan() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(1.44831, 0.158997);
+ TestUtils.assertEquals(expected, z.atan(), 1.0e-5);
+ }
+
+ public void testAtanInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.atan());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.atan());
+ TestUtils.assertSame(Complex.NaN, infOne.atan());
+ TestUtils.assertSame(Complex.NaN, negInfOne.atan());
+ TestUtils.assertSame(Complex.NaN, infInf.atan());
+ TestUtils.assertSame(Complex.NaN, infNegInf.atan());
+ TestUtils.assertSame(Complex.NaN, negInfInf.atan());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.atan());
+ }
+
+ public void testAtanNaN() {
+ assertTrue(Complex.NaN.atan().isNaN());
+ assertTrue(Complex.I.atan().isNaN());
+ }
+
+ public void testCos() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(-27.03495, -3.851153);
+ TestUtils.assertEquals(expected, z.cos(), 1.0e-5);
+ }
+
+ public void testCosNaN() {
+ assertTrue(Complex.NaN.cos().isNaN());
+ }
+
+ public void testCosInf() {
+ TestUtils.assertSame(infNegInf, oneInf.cos());
+ TestUtils.assertSame(infInf, oneNegInf.cos());
+ TestUtils.assertSame(Complex.NaN, infOne.cos());
+ TestUtils.assertSame(Complex.NaN, negInfOne.cos());
+ TestUtils.assertSame(Complex.NaN, infInf.cos());
+ TestUtils.assertSame(Complex.NaN, infNegInf.cos());
+ TestUtils.assertSame(Complex.NaN, negInfInf.cos());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.cos());
+ }
+
+ public void testCosh() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(-6.58066, -7.58155);
+ TestUtils.assertEquals(expected, z.cosh(), 1.0e-5);
+ }
+
+ public void testCoshNaN() {
+ assertTrue(Complex.NaN.cosh().isNaN());
+ }
+
+ public void testCoshInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.cosh());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.cosh());
+ TestUtils.assertSame(infInf, infOne.cosh());
+ TestUtils.assertSame(infNegInf, negInfOne.cosh());
+ TestUtils.assertSame(Complex.NaN, infInf.cosh());
+ TestUtils.assertSame(Complex.NaN, infNegInf.cosh());
+ TestUtils.assertSame(Complex.NaN, negInfInf.cosh());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.cosh());
+ }
+
+ public void testExp() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(-13.12878, -15.20078);
+ TestUtils.assertEquals(expected, z.exp(), 1.0e-5);
+ TestUtils.assertEquals(Complex.ONE,
+ Complex.ZERO.exp(), 10e-12);
+ Complex iPi = Complex.I.multiply(new Complex(pi,0));
+ TestUtils.assertEquals(Complex.ONE.negate(),
+ iPi.exp(), 10e-12);
+ }
+
+ public void testExpNaN() {
+ assertTrue(Complex.NaN.exp().isNaN());
+ }
+
+ public void testExpInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.exp());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.exp());
+ TestUtils.assertSame(infInf, infOne.exp());
+ TestUtils.assertSame(Complex.ZERO, negInfOne.exp());
+ TestUtils.assertSame(Complex.NaN, infInf.exp());
+ TestUtils.assertSame(Complex.NaN, infNegInf.exp());
+ TestUtils.assertSame(Complex.NaN, negInfInf.exp());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.exp());
+ }
+
+ public void testLog() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(1.60944, 0.927295);
+ TestUtils.assertEquals(expected, z.log(), 1.0e-5);
+ }
+
+ public void testLogNaN() {
+ assertTrue(Complex.NaN.log().isNaN());
+ }
+
+ public void testLogInf() {
+ TestUtils.assertEquals(new Complex(inf, pi / 2),
+ oneInf.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, -pi / 2),
+ oneNegInf.log(), 10e-12);
+ TestUtils.assertEquals(infZero, infOne.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, pi),
+ negInfOne.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, pi / 4),
+ infInf.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, -pi / 4),
+ infNegInf.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, 3d * pi / 4),
+ negInfInf.log(), 10e-12);
+ TestUtils.assertEquals(new Complex(inf, - 3d * pi / 4),
+ negInfNegInf.log(), 10e-12);
+ }
+
+ public void testLogZero() {
+ TestUtils.assertSame(negInfZero, Complex.ZERO.log());
+ }
+
+ public void testPow() {
+ Complex x = new Complex(3, 4);
+ Complex y = new Complex(5, 6);
+ Complex expected = new Complex(-1.860893, 11.83677);
+ TestUtils.assertEquals(expected, x.pow(y), 1.0e-5);
+ }
+
+ public void testPowNaNBase() {
+ Complex x = new Complex(3, 4);
+ assertTrue(Complex.NaN.pow(x).isNaN());
+ }
+
+ public void testPowNaNExponent() {
+ Complex x = new Complex(3, 4);
+ assertTrue(x.pow(Complex.NaN).isNaN());
+ }
+
+ public void testPowInf() {
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(oneInf));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(oneNegInf));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(infOne));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(infInf));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(infNegInf));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(negInfInf));
+ TestUtils.assertSame(Complex.NaN,Complex.ONE.pow(negInfNegInf));
+ TestUtils.assertSame(Complex.NaN,infOne.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,negInfOne.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,infInf.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,infNegInf.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,negInfInf.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,negInfNegInf.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,negInfNegInf.pow(infNegInf));
+ TestUtils.assertSame(Complex.NaN,negInfNegInf.pow(negInfNegInf));
+ TestUtils.assertSame(Complex.NaN,negInfNegInf.pow(infInf));
+ TestUtils.assertSame(Complex.NaN,infInf.pow(infNegInf));
+ TestUtils.assertSame(Complex.NaN,infInf.pow(negInfNegInf));
+ TestUtils.assertSame(Complex.NaN,infInf.pow(infInf));
+ TestUtils.assertSame(Complex.NaN,infNegInf.pow(infNegInf));
+ TestUtils.assertSame(Complex.NaN,infNegInf.pow(negInfNegInf));
+ TestUtils.assertSame(Complex.NaN,infNegInf.pow(infInf));
+ }
+
+ public void testPowZero() {
+ TestUtils.assertSame(Complex.NaN,
+ Complex.ZERO.pow(Complex.ONE));
+ TestUtils.assertSame(Complex.NaN,
+ Complex.ZERO.pow(Complex.ZERO));
+ TestUtils.assertSame(Complex.NaN,
+ Complex.ZERO.pow(Complex.I));
+ TestUtils.assertEquals(Complex.ONE,
+ Complex.ONE.pow(Complex.ZERO), 10e-12);
+ TestUtils.assertEquals(Complex.ONE,
+ Complex.I.pow(Complex.ZERO), 10e-12);
+ TestUtils.assertEquals(Complex.ONE,
+ new Complex(-1, 3).pow(Complex.ZERO), 10e-12);
+ }
+
+ public void testpowNull() {
+ try {
+ Complex.ONE.pow(null);
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testSin() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(3.853738, -27.01681);
+ TestUtils.assertEquals(expected, z.sin(), 1.0e-5);
+ }
+
+ public void testSinInf() {
+ TestUtils.assertSame(infInf, oneInf.sin());
+ TestUtils.assertSame(infNegInf, oneNegInf.sin());
+ TestUtils.assertSame(Complex.NaN, infOne.sin());
+ TestUtils.assertSame(Complex.NaN, negInfOne.sin());
+ TestUtils.assertSame(Complex.NaN, infInf.sin());
+ TestUtils.assertSame(Complex.NaN, infNegInf.sin());
+ TestUtils.assertSame(Complex.NaN, negInfInf.sin());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.sin());
+ }
+
+ public void testSinNaN() {
+ assertTrue(Complex.NaN.sin().isNaN());
+ }
+
+ public void testSinh() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(-6.54812, -7.61923);
+ TestUtils.assertEquals(expected, z.sinh(), 1.0e-5);
+ }
+
+ public void testSinhNaN() {
+ assertTrue(Complex.NaN.sinh().isNaN());
+ }
+
+ public void testSinhInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.sinh());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.sinh());
+ TestUtils.assertSame(infInf, infOne.sinh());
+ TestUtils.assertSame(negInfInf, negInfOne.sinh());
+ TestUtils.assertSame(Complex.NaN, infInf.sinh());
+ TestUtils.assertSame(Complex.NaN, infNegInf.sinh());
+ TestUtils.assertSame(Complex.NaN, negInfInf.sinh());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.sinh());
+ }
+
+ public void testSqrtRealPositive() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(2, 1);
+ TestUtils.assertEquals(expected, z.sqrt(), 1.0e-5);
+ }
+
+ public void testSqrtRealZero() {
+ Complex z = new Complex(0.0, 4);
+ Complex expected = new Complex(1.41421, 1.41421);
+ TestUtils.assertEquals(expected, z.sqrt(), 1.0e-5);
+ }
+
+ public void testSqrtRealNegative() {
+ Complex z = new Complex(-3.0, 4);
+ Complex expected = new Complex(1, 2);
+ TestUtils.assertEquals(expected, z.sqrt(), 1.0e-5);
+ }
+
+ public void testSqrtImaginaryZero() {
+ Complex z = new Complex(-3.0, 0.0);
+ Complex expected = new Complex(0.0, 1.73205);
+ TestUtils.assertEquals(expected, z.sqrt(), 1.0e-5);
+ }
+
+ public void testSqrtImaginaryNegative() {
+ Complex z = new Complex(-3.0, -4.0);
+ Complex expected = new Complex(1.0, -2.0);
+ TestUtils.assertEquals(expected, z.sqrt(), 1.0e-5);
+ }
+
+ public void testSqrtPolar() {
+ double r = 1;
+ for (int i = 0; i < 5; i++) {
+ r += i;
+ double theta = 0;
+ for (int j =0; j < 11; j++) {
+ theta += pi /12;
+ Complex z = ComplexUtils.polar2Complex(r, theta);
+ Complex sqrtz = ComplexUtils.polar2Complex(FastMath.sqrt(r), theta / 2);
+ TestUtils.assertEquals(sqrtz, z.sqrt(), 10e-12);
+ }
+ }
+ }
+
+ public void testSqrtNaN() {
+ assertTrue(Complex.NaN.sqrt().isNaN());
+ }
+
+ public void testSqrtInf() {
+ TestUtils.assertSame(infNaN, oneInf.sqrt());
+ TestUtils.assertSame(infNaN, oneNegInf.sqrt());
+ TestUtils.assertSame(infZero, infOne.sqrt());
+ TestUtils.assertSame(zeroInf, negInfOne.sqrt());
+ TestUtils.assertSame(infNaN, infInf.sqrt());
+ TestUtils.assertSame(infNaN, infNegInf.sqrt());
+ TestUtils.assertSame(nanInf, negInfInf.sqrt());
+ TestUtils.assertSame(nanNegInf, negInfNegInf.sqrt());
+ }
+
+ public void testSqrt1z() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(4.08033, -2.94094);
+ TestUtils.assertEquals(expected, z.sqrt1z(), 1.0e-5);
+ }
+
+ public void testSqrt1zNaN() {
+ assertTrue(Complex.NaN.sqrt1z().isNaN());
+ }
+
+ public void testTan() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(-0.000187346, 0.999356);
+ TestUtils.assertEquals(expected, z.tan(), 1.0e-5);
+ }
+
+ public void testTanNaN() {
+ assertTrue(Complex.NaN.tan().isNaN());
+ }
+
+ public void testTanInf() {
+ TestUtils.assertSame(zeroNaN, oneInf.tan());
+ TestUtils.assertSame(zeroNaN, oneNegInf.tan());
+ TestUtils.assertSame(Complex.NaN, infOne.tan());
+ TestUtils.assertSame(Complex.NaN, negInfOne.tan());
+ TestUtils.assertSame(Complex.NaN, infInf.tan());
+ TestUtils.assertSame(Complex.NaN, infNegInf.tan());
+ TestUtils.assertSame(Complex.NaN, negInfInf.tan());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.tan());
+ }
+
+ public void testTanCritical() {
+ TestUtils.assertSame(infNaN, new Complex(pi/2, 0).tan());
+ TestUtils.assertSame(negInfNaN, new Complex(-pi/2, 0).tan());
+ }
+
+ public void testTanh() {
+ Complex z = new Complex(3, 4);
+ Complex expected = new Complex(1.00071, 0.00490826);
+ TestUtils.assertEquals(expected, z.tanh(), 1.0e-5);
+ }
+
+ public void testTanhNaN() {
+ assertTrue(Complex.NaN.tanh().isNaN());
+ }
+
+ public void testTanhInf() {
+ TestUtils.assertSame(Complex.NaN, oneInf.tanh());
+ TestUtils.assertSame(Complex.NaN, oneNegInf.tanh());
+ TestUtils.assertSame(nanZero, infOne.tanh());
+ TestUtils.assertSame(nanZero, negInfOne.tanh());
+ TestUtils.assertSame(Complex.NaN, infInf.tanh());
+ TestUtils.assertSame(Complex.NaN, infNegInf.tanh());
+ TestUtils.assertSame(Complex.NaN, negInfInf.tanh());
+ TestUtils.assertSame(Complex.NaN, negInfNegInf.tanh());
+ }
+
+ public void testTanhCritical() {
+ TestUtils.assertSame(nanInf, new Complex(0, pi/2).tanh());
+ }
+
+ /** test issue MATH-221 */
+ public void testMath221() {
+ assertEquals(new Complex(0,-1), new Complex(0,1).multiply(new Complex(-1,0)));
+ }
+
+ /**
+ * Test: computing <b>third roots</b> of z.
+ * <pre>
+ * <code>
+ * <b>z = -2 + 2 * i</b>
+ * => z_0 = 1 + i
+ * => z_1 = -1.3660 + 0.3660 * i
+ * => z_2 = 0.3660 - 1.3660 * i
+ * </code>
+ * </pre>
+ */
+ public void testNthRoot_normal_thirdRoot() {
+ // The complex number we want to compute all third-roots for.
+ Complex z = new Complex(-2,2);
+ // The List holding all third roots
+ Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
+ // Returned Collection must not be empty!
+ assertEquals(3, thirdRootsOfZ.length);
+ // test z_0
+ assertEquals(1.0, thirdRootsOfZ[0].getReal(), 1.0e-5);
+ assertEquals(1.0, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
+ // test z_1
+ assertEquals(-1.3660254037844386, thirdRootsOfZ[1].getReal(), 1.0e-5);
+ assertEquals(0.36602540378443843, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
+ // test z_2
+ assertEquals(0.366025403784439, thirdRootsOfZ[2].getReal(), 1.0e-5);
+ assertEquals(-1.3660254037844384, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
+ }
+
+
+ /**
+ * Test: computing <b>fourth roots</b> of z.
+ * <pre>
+ * <code>
+ * <b>z = 5 - 2 * i</b>
+ * => z_0 = 1.5164 - 0.1446 * i
+ * => z_1 = 0.1446 + 1.5164 * i
+ * => z_2 = -1.5164 + 0.1446 * i
+ * => z_3 = -1.5164 - 0.1446 * i
+ * </code>
+ * </pre>
+ */
+ public void testNthRoot_normal_fourthRoot() {
+ // The complex number we want to compute all third-roots for.
+ Complex z = new Complex(5,-2);
+ // The List holding all fourth roots
+ Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]);
+ // Returned Collection must not be empty!
+ assertEquals(4, fourthRootsOfZ.length);
+ // test z_0
+ assertEquals(1.5164629308487783, fourthRootsOfZ[0].getReal(), 1.0e-5);
+ assertEquals(-0.14469266210702247, fourthRootsOfZ[0].getImaginary(), 1.0e-5);
+ // test z_1
+ assertEquals(0.14469266210702256, fourthRootsOfZ[1].getReal(), 1.0e-5);
+ assertEquals(1.5164629308487783, fourthRootsOfZ[1].getImaginary(), 1.0e-5);
+ // test z_2
+ assertEquals(-1.5164629308487783, fourthRootsOfZ[2].getReal(), 1.0e-5);
+ assertEquals(0.14469266210702267, fourthRootsOfZ[2].getImaginary(), 1.0e-5);
+ // test z_3
+ assertEquals(-0.14469266210702275, fourthRootsOfZ[3].getReal(), 1.0e-5);
+ assertEquals(-1.5164629308487783, fourthRootsOfZ[3].getImaginary(), 1.0e-5);
+ }
+
+ /**
+ * Test: computing <b>third roots</b> of z.
+ * <pre>
+ * <code>
+ * <b>z = 8</b>
+ * => z_0 = 2
+ * => z_1 = -1 + 1.73205 * i
+ * => z_2 = -1 - 1.73205 * i
+ * </code>
+ * </pre>
+ */
+ public void testNthRoot_cornercase_thirdRoot_imaginaryPartEmpty() {
+ // The number 8 has three third roots. One we all already know is the number 2.
+ // But there are two more complex roots.
+ Complex z = new Complex(8,0);
+ // The List holding all third roots
+ Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
+ // Returned Collection must not be empty!
+ assertEquals(3, thirdRootsOfZ.length);
+ // test z_0
+ assertEquals(2.0, thirdRootsOfZ[0].getReal(), 1.0e-5);
+ assertEquals(0.0, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
+ // test z_1
+ assertEquals(-1.0, thirdRootsOfZ[1].getReal(), 1.0e-5);
+ assertEquals(1.7320508075688774, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
+ // test z_2
+ assertEquals(-1.0, thirdRootsOfZ[2].getReal(), 1.0e-5);
+ assertEquals(-1.732050807568877, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
+ }
+
+
+ /**
+ * Test: computing <b>third roots</b> of z with real part 0.
+ * <pre>
+ * <code>
+ * <b>z = 2 * i</b>
+ * => z_0 = 1.0911 + 0.6299 * i
+ * => z_1 = -1.0911 + 0.6299 * i
+ * => z_2 = -2.3144 - 1.2599 * i
+ * </code>
+ * </pre>
+ */
+ public void testNthRoot_cornercase_thirdRoot_realPartZero() {
+ // complex number with only imaginary part
+ Complex z = new Complex(0,2);
+ // The List holding all third roots
+ Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
+ // Returned Collection must not be empty!
+ assertEquals(3, thirdRootsOfZ.length);
+ // test z_0
+ assertEquals(1.0911236359717216, thirdRootsOfZ[0].getReal(), 1.0e-5);
+ assertEquals(0.6299605249474365, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
+ // test z_1
+ assertEquals(-1.0911236359717216, thirdRootsOfZ[1].getReal(), 1.0e-5);
+ assertEquals(0.6299605249474365, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
+ // test z_2
+ assertEquals(-2.3144374213981936E-16, thirdRootsOfZ[2].getReal(), 1.0e-5);
+ assertEquals(-1.2599210498948732, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
+ }
+
+ /**
+ * Test cornercases with NaN and Infinity.
+ */
+ public void testNthRoot_cornercase_NAN_Inf() {
+ // NaN + finite -> NaN
+ List<Complex> roots = oneNaN.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ roots = nanZero.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ // NaN + infinite -> NaN
+ roots = nanInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ // finite + infinite -> Inf
+ roots = oneInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.INF, roots.get(0));
+
+ // infinite + infinite -> Inf
+ roots = negInfInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.INF, roots.get(0));
+ }
+
+ /**
+ * Test standard values
+ */
+ public void testGetArgument() {
+ Complex z = new Complex(1, 0);
+ assertEquals(0.0, z.getArgument(), 1.0e-12);
+
+ z = new Complex(1, 1);
+ assertEquals(FastMath.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(0, 1);
+ assertEquals(FastMath.PI/2, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, 1);
+ assertEquals(3 * FastMath.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, 0);
+ assertEquals(FastMath.PI, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, -1);
+ assertEquals(-3 * FastMath.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(0, -1);
+ assertEquals(-FastMath.PI/2, z.getArgument(), 1.0e-12);
+
+ z = new Complex(1, -1);
+ assertEquals(-FastMath.PI/4, z.getArgument(), 1.0e-12);
+
+ }
+
+ /**
+ * Verify atan2-style handling of infinite parts
+ */
+ public void testGetArgumentInf() {
+ assertEquals(FastMath.PI/4, infInf.getArgument(), 1.0e-12);
+ assertEquals(FastMath.PI/2, oneInf.getArgument(), 1.0e-12);
+ assertEquals(0.0, infOne.getArgument(), 1.0e-12);
+ assertEquals(FastMath.PI/2, zeroInf.getArgument(), 1.0e-12);
+ assertEquals(0.0, infZero.getArgument(), 1.0e-12);
+ assertEquals(FastMath.PI, negInfOne.getArgument(), 1.0e-12);
+ assertEquals(-3.0*FastMath.PI/4, negInfNegInf.getArgument(), 1.0e-12);
+ assertEquals(-FastMath.PI/2, oneNegInf.getArgument(), 1.0e-12);
+ }
+
+ /**
+ * Verify that either part NaN results in NaN
+ */
+ public void testGetArgumentNaN() {
+ assertEquals(nan, nanZero.getArgument());
+ assertEquals(nan, zeroNaN.getArgument());
+ assertEquals(nan, Complex.NaN.getArgument());
+ }
+
+ public void testSerial() {
+ Complex z = new Complex(3.0, 4.0);
+ assertEquals(z, TestUtils.serializeAndRecover(z));
+ Complex ncmplx = (Complex)TestUtils.serializeAndRecover(oneNaN);
+ assertEquals(nanZero, ncmplx);
+ assertTrue(ncmplx.isNaN());
+ Complex infcmplx = (Complex)TestUtils.serializeAndRecover(infInf);
+ assertEquals(infInf, infcmplx);
+ assertTrue(infcmplx.isInfinite());
+ TestComplex tz = new TestComplex(3.0, 4.0);
+ assertEquals(tz, TestUtils.serializeAndRecover(tz));
+ TestComplex ntcmplx = (TestComplex)TestUtils.serializeAndRecover(new TestComplex(oneNaN));
+ assertEquals(nanZero, ntcmplx);
+ assertTrue(ntcmplx.isNaN());
+ TestComplex inftcmplx = (TestComplex)TestUtils.serializeAndRecover(new TestComplex(infInf));
+ assertEquals(infInf, inftcmplx);
+ assertTrue(inftcmplx.isInfinite());
+ }
+
+ /**
+ * Class to test extending Complex
+ */
+ public static class TestComplex extends Complex {
+
+ /**
+ * Serialization identifier.
+ */
+ private static final long serialVersionUID = 3268726724160389237L;
+
+ public TestComplex(double real, double imaginary) {
+ super(real, imaginary);
+ }
+
+ public TestComplex(Complex other){
+ this(other.getReal(), other.getImaginary());
+ }
+
+ @Override
+ protected TestComplex createComplex(double real, double imaginary){
+ return new TestComplex(real, imaginary);
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/complex/ComplexUtilsTest.java b/src/test/java/org/apache/commons/math/complex/ComplexUtilsTest.java
new file mode 100644
index 0000000..e19f59f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/ComplexUtilsTest.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.complex;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class ComplexUtilsTest extends TestCase {
+
+ private double inf = Double.POSITIVE_INFINITY;
+ private double negInf = Double.NEGATIVE_INFINITY;
+ private double nan = Double.NaN;
+ private double pi = FastMath.PI;
+
+ private Complex negInfInf = new Complex(negInf, inf);
+ private Complex infNegInf = new Complex(inf, negInf);
+ private Complex infInf = new Complex(inf, inf);
+ private Complex negInfNegInf = new Complex(negInf, negInf);
+ private Complex infNaN = new Complex(inf, nan);
+
+ public void testPolar2Complex() {
+ TestUtils.assertEquals(Complex.ONE,
+ ComplexUtils.polar2Complex(1, 0), 10e-12);
+ TestUtils.assertEquals(Complex.ZERO,
+ ComplexUtils.polar2Complex(0, 1), 10e-12);
+ TestUtils.assertEquals(Complex.ZERO,
+ ComplexUtils.polar2Complex(0, -1), 10e-12);
+ TestUtils.assertEquals(Complex.I,
+ ComplexUtils.polar2Complex(1, pi/2), 10e-12);
+ TestUtils.assertEquals(Complex.I.negate(),
+ ComplexUtils.polar2Complex(1, -pi/2), 10e-12);
+ double r = 0;
+ for (int i = 0; i < 5; i++) {
+ r += i;
+ double theta = 0;
+ for (int j =0; j < 20; j++) {
+ theta += pi / 6;
+ TestUtils.assertEquals(altPolar(r, theta),
+ ComplexUtils.polar2Complex(r, theta), 10e-12);
+ }
+ theta = -2 * pi;
+ for (int j =0; j < 20; j++) {
+ theta -= pi / 6;
+ TestUtils.assertEquals(altPolar(r, theta),
+ ComplexUtils.polar2Complex(r, theta), 10e-12);
+ }
+ }
+ }
+
+ protected Complex altPolar(double r, double theta) {
+ return Complex.I.multiply(new Complex(theta, 0)).exp().multiply(new Complex(r, 0));
+ }
+
+ public void testPolar2ComplexIllegalModulus() {
+ try {
+ ComplexUtils.polar2Complex(-1, 0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testPolar2ComplexNaN() {
+ TestUtils.assertSame(Complex.NaN, ComplexUtils.polar2Complex(nan, 1));
+ TestUtils.assertSame(Complex.NaN, ComplexUtils.polar2Complex(1, nan));
+ TestUtils.assertSame(Complex.NaN,
+ ComplexUtils.polar2Complex(nan, nan));
+ }
+
+ public void testPolar2ComplexInf() {
+ TestUtils.assertSame(Complex.NaN, ComplexUtils.polar2Complex(1, inf));
+ TestUtils.assertSame(Complex.NaN,
+ ComplexUtils.polar2Complex(1, negInf));
+ TestUtils.assertSame(Complex.NaN, ComplexUtils.polar2Complex(inf, inf));
+ TestUtils.assertSame(Complex.NaN,
+ ComplexUtils.polar2Complex(inf, negInf));
+ TestUtils.assertSame(infInf, ComplexUtils.polar2Complex(inf, pi/4));
+ TestUtils.assertSame(infNaN, ComplexUtils.polar2Complex(inf, 0));
+ TestUtils.assertSame(infNegInf, ComplexUtils.polar2Complex(inf, -pi/4));
+ TestUtils.assertSame(negInfInf, ComplexUtils.polar2Complex(inf, 3*pi/4));
+ TestUtils.assertSame(negInfNegInf, ComplexUtils.polar2Complex(inf, 5*pi/4));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/complex/FrenchComplexFormatTest.java b/src/test/java/org/apache/commons/math/complex/FrenchComplexFormatTest.java
new file mode 100644
index 0000000..9fff723
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/complex/FrenchComplexFormatTest.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.complex;
+
+import java.util.Locale;
+
+
+public class FrenchComplexFormatTest extends ComplexFormatAbstractTest {
+
+ @Override
+ protected char getDecimalCharacter() {
+ return ',';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.FRENCH;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/dfp/Decimal10.java b/src/test/java/org/apache/commons/math/dfp/Decimal10.java
new file mode 100644
index 0000000..f86c059
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/dfp/Decimal10.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.dfp;
+
+public class Decimal10 extends DfpDec {
+
+ Decimal10(final DfpField factory) {
+ super(factory);
+ }
+
+ Decimal10(final DfpField factory, final byte x) {
+ super(factory, x);
+ }
+
+ Decimal10(final DfpField factory, final int x) {
+ super(factory, x);
+ }
+
+ Decimal10(final DfpField factory, final long x) {
+ super(factory, x);
+ }
+
+ Decimal10(final DfpField factory, final double x) {
+ super(factory, x);
+ }
+
+ public Decimal10(final Dfp d) {
+ super(d);
+ }
+
+ public Decimal10(final DfpField factory, final String s) {
+ super(factory, s);
+ }
+
+ protected Decimal10(final DfpField factory, final byte sign, final byte nans) {
+ super(factory, sign, nans);
+ }
+
+ @Override
+ public Dfp newInstance() {
+ return new Decimal10(getField());
+ }
+
+ @Override
+ public Dfp newInstance(final byte x) {
+ return new Decimal10(getField(), x);
+ }
+
+ @Override
+ public Dfp newInstance(final int x) {
+ return new Decimal10(getField(), x);
+ }
+
+ @Override
+ public Dfp newInstance(final long x) {
+ return new Decimal10(getField(), x);
+ }
+
+ @Override
+ public Dfp newInstance(final double x) {
+ return new Decimal10(getField(), x);
+ }
+
+ @Override
+ public Dfp newInstance(final Dfp d) {
+ return new Decimal10(d);
+ }
+
+ @Override
+ public Dfp newInstance(final String s) {
+ return new Decimal10(getField(), s);
+ }
+
+ @Override
+ public Dfp newInstance(final byte sign, final byte nans) {
+ return new Decimal10(getField(), sign, nans);
+ }
+
+ @Override
+ protected int getDecimalDigits() {
+ return 10;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/dfp/DfpDecTest.java b/src/test/java/org/apache/commons/math/dfp/DfpDecTest.java
new file mode 100644
index 0000000..0cf6b22
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/dfp/DfpDecTest.java
@@ -0,0 +1,565 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.dfp;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfpDecTest {
+
+ private DfpField field;
+ private Dfp pinf;
+ private Dfp ninf;
+
+ @Before
+ public void setUp() {
+ // Some basic setup. Define some constants and clear the status flags
+ field = new DfpField(20);
+ pinf = new DfpDec(field, 1).divide(new DfpDec(field, 0));
+ ninf = new DfpDec(field, -1).divide(new DfpDec(field, 0));
+ ninf.getField().clearIEEEFlags();
+ }
+
+ @After
+ public void tearDown() {
+ field = null;
+ pinf = null;
+ ninf = null;
+ }
+
+ // Generic test function. Takes params x and y and tests them for
+ // equality. Then checks the status flags against the flags argument.
+ // If the test fail, it prints the desc string
+ private void test(Dfp x, Dfp y, int flags, String desc) {
+ boolean b = x.equals(y);
+
+ if (!x.equals(y) && !x.unequal(y)) // NaNs involved
+ b = (x.toString().equals(y.toString()));
+
+ if (x.equals(new DfpDec(field, 0))) // distinguish +/- zero
+ b = (b && (x.toString().equals(y.toString())));
+
+ b = (b && x.getField().getIEEEFlags() == flags);
+
+ if (!b)
+ Assert.assertTrue("assersion failed "+desc+" x = "+x.toString()+" flags = "+x.getField().getIEEEFlags(), b);
+
+ x.getField().clearIEEEFlags();
+ }
+
+ @Test
+ public void testRound()
+ {
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_EVEN);
+
+ test(new DfpDec(field, "12345678901234567890"),
+ new DfpDec(field, "12345678901234568000"),
+ DfpField.FLAG_INEXACT, "Round #1");
+
+ test(new DfpDec(field, "0.12345678901234567890"),
+ new DfpDec(field, "0.12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #2");
+
+ test(new DfpDec(field, "0.12345678901234567500"),
+ new DfpDec(field, "0.12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #3");
+
+ test(new DfpDec(field, "0.12345678901234568500"),
+ new DfpDec(field, "0.12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #4");
+
+ test(new DfpDec(field, "0.12345678901234568501"),
+ new DfpDec(field, "0.12345678901234569"),
+ DfpField.FLAG_INEXACT, "Round #5");
+
+ test(new DfpDec(field, "0.12345678901234568499"),
+ new DfpDec(field, "0.12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #6");
+
+ test(new DfpDec(field, "1.2345678901234567890"),
+ new DfpDec(field, "1.2345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #7");
+
+ test(new DfpDec(field, "1.2345678901234567500"),
+ new DfpDec(field, "1.2345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #8");
+
+ test(new DfpDec(field, "1.2345678901234568500"),
+ new DfpDec(field, "1.2345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #9");
+
+ test(new DfpDec(field, "1.2345678901234568000").add(new DfpDec(field, ".0000000000000000501")),
+ new DfpDec(field, "1.2345678901234569"),
+ DfpField.FLAG_INEXACT, "Round #10");
+
+ test(new DfpDec(field, "1.2345678901234568499"),
+ new DfpDec(field, "1.2345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #11");
+
+ test(new DfpDec(field, "12.345678901234567890"),
+ new DfpDec(field, "12.345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #12");
+
+ test(new DfpDec(field, "12.345678901234567500"),
+ new DfpDec(field, "12.345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #13");
+
+ test(new DfpDec(field, "12.345678901234568500"),
+ new DfpDec(field, "12.345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #14");
+
+ test(new DfpDec(field, "12.345678901234568").add(new DfpDec(field, ".000000000000000501")),
+ new DfpDec(field, "12.345678901234569"),
+ DfpField.FLAG_INEXACT, "Round #15");
+
+ test(new DfpDec(field, "12.345678901234568499"),
+ new DfpDec(field, "12.345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #16");
+
+ test(new DfpDec(field, "123.45678901234567890"),
+ new DfpDec(field, "123.45678901234568"),
+ DfpField.FLAG_INEXACT, "Round #17");
+
+ test(new DfpDec(field, "123.45678901234567500"),
+ new DfpDec(field, "123.45678901234568"),
+ DfpField.FLAG_INEXACT, "Round #18");
+
+ test(new DfpDec(field, "123.45678901234568500"),
+ new DfpDec(field, "123.45678901234568"),
+ DfpField.FLAG_INEXACT, "Round #19");
+
+ test(new DfpDec(field, "123.456789012345685").add(new DfpDec(field, ".00000000000000501")),
+ new DfpDec(field, "123.45678901234569"),
+ DfpField.FLAG_INEXACT, "Round #20");
+
+ test(new DfpDec(field, "123.45678901234568499"),
+ new DfpDec(field, "123.45678901234568"),
+ DfpField.FLAG_INEXACT, "Round #21");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_DOWN);
+
+ // Round down
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.9")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #22");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.99999999")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #23");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.99999999")),
+ new DfpDec(field, "-12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #24");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_UP);
+
+ // Round up
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.1")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #25");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.0001")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #26");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.1")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #27");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.0001")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #28");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "0")),
+ new DfpDec(field, "-12345678901234567"),
+ 0, "Round #28.5");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_UP);
+
+ // Round half up
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.499999999999")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #29");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.50000001")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #30");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.5")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #30.5");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.499999999999")),
+ new DfpDec(field, "-12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #31");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.50000001")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #32");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_DOWN);
+
+ // Round half down
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.5001")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #33");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.5000")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #34");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.5001")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #35");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.6")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #35.5");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.5000")),
+ new DfpDec(field, "-12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #36");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_ODD);
+
+ // Round half odd
+ test(new DfpDec(field, "12345678901234568").add(new DfpDec(field, "0.5000")),
+ new DfpDec(field, "12345678901234569"),
+ DfpField.FLAG_INEXACT, "Round #37");
+
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.5000")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #38");
+
+ test(new DfpDec(field, "-12345678901234568").add(new DfpDec(field, "-0.5000")),
+ new DfpDec(field, "-12345678901234569"),
+ DfpField.FLAG_INEXACT, "Round #39");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.5000")),
+ new DfpDec(field, "-12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #40");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_CEIL);
+
+ // Round ceil
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.0001")),
+ new DfpDec(field, "12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #41");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.9999")),
+ new DfpDec(field, "-12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #42");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_FLOOR);
+
+ // Round floor
+ test(new DfpDec(field, "12345678901234567").add(new DfpDec(field, "0.9999")),
+ new DfpDec(field, "12345678901234567"),
+ DfpField.FLAG_INEXACT, "Round #43");
+
+ test(new DfpDec(field, "-12345678901234567").add(new DfpDec(field, "-0.0001")),
+ new DfpDec(field, "-12345678901234568"),
+ DfpField.FLAG_INEXACT, "Round #44");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_EVEN); // reset
+ }
+
+ @Test
+ public void testRoundDecimal10()
+ {
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_EVEN);
+
+ test(new Decimal10(field, "1234567891234567890"),
+ new Decimal10(field, "1234567891000000000"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #1");
+
+ test(new Decimal10(field, "0.1234567891634567890"),
+ new Decimal10(field, "0.1234567892"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #2");
+
+ test(new Decimal10(field, "0.1234567891500000000"),
+ new Decimal10(field, "0.1234567892"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #3");
+
+ test(new Decimal10(field, "0.1234567890500"),
+ new Decimal10(field, "0.1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #4");
+
+ test(new Decimal10(field, "0.1234567890501"),
+ new Decimal10(field, "0.1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #5");
+
+ test(new Decimal10(field, "0.1234567890499"),
+ new Decimal10(field, "0.1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #6");
+
+ test(new Decimal10(field, "1.234567890890"),
+ new Decimal10(field, "1.234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #7");
+
+ test(new Decimal10(field, "1.234567891500"),
+ new Decimal10(field, "1.234567892"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #8");
+
+ test(new Decimal10(field, "1.234567890500"),
+ new Decimal10(field, "1.234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #9");
+
+ test(new Decimal10(field, "1.234567890000").add(new Decimal10(field, ".000000000501")),
+ new Decimal10(field, "1.234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #10");
+
+ test(new Decimal10(field, "1.234567890499"),
+ new Decimal10(field, "1.234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #11");
+
+ test(new Decimal10(field, "12.34567890890"),
+ new Decimal10(field, "12.34567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #12");
+
+ test(new Decimal10(field, "12.34567891500"),
+ new Decimal10(field, "12.34567892"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #13");
+
+ test(new Decimal10(field, "12.34567890500"),
+ new Decimal10(field, "12.34567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #14");
+
+ test(new Decimal10(field, "12.34567890").add(new Decimal10(field, ".00000000501")),
+ new Decimal10(field, "12.34567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #15");
+
+ test(new Decimal10(field, "12.34567890499"),
+ new Decimal10(field, "12.34567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #16");
+
+ test(new Decimal10(field, "123.4567890890"),
+ new Decimal10(field, "123.4567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #17");
+
+ test(new Decimal10(field, "123.4567891500"),
+ new Decimal10(field, "123.4567892"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #18");
+
+ test(new Decimal10(field, "123.4567890500"),
+ new Decimal10(field, "123.4567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #19");
+
+ test(new Decimal10(field, "123.4567890").add(new Decimal10(field, ".0000000501")),
+ new Decimal10(field, "123.4567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #20");
+
+ test(new Decimal10(field, "123.4567890499"),
+ new Decimal10(field, "123.4567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #21");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_DOWN);
+
+ // RoundDecimal10 down
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.9")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #22");
+
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.99999999")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #23");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.99999999")),
+ new Decimal10(field, "-1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #24");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_UP);
+
+ // RoundDecimal10 up
+ test(new Decimal10(field, 1234567890).add(new Decimal10(field, "0.1")),
+ new Decimal10(field, 1234567891l),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #25");
+
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.0001")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #26");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.1")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #27");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.0001")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #28");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "0")),
+ new Decimal10(field, "-1234567890"),
+ 0, "RoundDecimal10 #28.5");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_UP);
+
+ // RoundDecimal10 half up
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.4999999999")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #29");
+
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.50000001")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #30");
+
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.5")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #30.5");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.4999999999")),
+ new Decimal10(field, "-1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #31");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.50000001")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #32");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_DOWN);
+
+ // RoundDecimal10 half down
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.5001")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #33");
+
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.5000")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #34");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.5001")),
+ new Decimal10(field, "-1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #35");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.6")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #35.5");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.5000")),
+ new Decimal10(field, "-1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #36");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_ODD);
+
+ // RoundDecimal10 half odd
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.5000")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #37");
+
+ test(new Decimal10(field, "1234567891").add(new Decimal10(field, "0.5000")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #38");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.5000")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #39");
+
+ test(new Decimal10(field, "-1234567891").add(new Decimal10(field, "-0.5000")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #40");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_CEIL);
+
+ // RoundDecimal10 ceil
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.0001")),
+ new Decimal10(field, "1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #41");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.9999")),
+ new Decimal10(field, "-1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #42");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_FLOOR);
+
+ // RoundDecimal10 floor
+ test(new Decimal10(field, "1234567890").add(new Decimal10(field, "0.9999")),
+ new Decimal10(field, "1234567890"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #43");
+
+ test(new Decimal10(field, "-1234567890").add(new Decimal10(field, "-0.0001")),
+ new Decimal10(field, "-1234567891"),
+ DfpField.FLAG_INEXACT, "RoundDecimal10 #44");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_EVEN); // reset
+ }
+
+ @Test
+ public void testNextAfter()
+ {
+ test(new DfpDec(field, 1).nextAfter(pinf),
+ new DfpDec(field, "1.0000000000000001"),
+ 0, "NextAfter #1");
+
+ test(new DfpDec(field, "1.0000000000000001").nextAfter(ninf),
+ new DfpDec(field, 1),
+ 0, "NextAfter #1.5");
+
+ test(new DfpDec(field, 1).nextAfter(ninf),
+ new DfpDec(field, "0.99999999999999999"),
+ 0, "NextAfter #2");
+
+ test(new DfpDec(field, "0.99999999999999999").nextAfter(new DfpDec(field, 2)),
+ new DfpDec(field, 1),
+ 0, "NextAfter #3");
+
+ test(new DfpDec(field, -1).nextAfter(ninf),
+ new DfpDec(field, "-1.0000000000000001"),
+ 0, "NextAfter #4");
+
+ test(new DfpDec(field, -1).nextAfter(pinf),
+ new DfpDec(field, "-0.99999999999999999"),
+ 0, "NextAfter #5");
+
+ test(new DfpDec(field, "-0.99999999999999999").nextAfter(new DfpDec(field, -2)),
+ new DfpDec(field, (byte) -1),
+ 0, "NextAfter #6");
+
+ test(new DfpDec(field, (byte) 2).nextAfter(new DfpDec(field, 2)),
+ new DfpDec(field, 2l),
+ 0, "NextAfter #7");
+
+ test(new DfpDec(field, 0).nextAfter(new DfpDec(field, 0)),
+ new DfpDec(field, 0),
+ 0, "NextAfter #8");
+
+ test(new DfpDec(field, -2).nextAfter(new DfpDec(field, -2)),
+ new DfpDec(field, -2),
+ 0, "NextAfter #9");
+
+ test(new DfpDec(field, 0).nextAfter(new DfpDec(field, 1)),
+ new DfpDec(field, "1e-131092"),
+ DfpField.FLAG_UNDERFLOW, "NextAfter #10");
+
+ test(new DfpDec(field, 0).nextAfter(new DfpDec(field, -1)),
+ new DfpDec(field, "-1e-131092"),
+ DfpField.FLAG_UNDERFLOW, "NextAfter #11");
+
+ test(new DfpDec(field, "-1e-131092").nextAfter(pinf),
+ new DfpDec(field, "-0"),
+ DfpField.FLAG_UNDERFLOW|DfpField.FLAG_INEXACT, "Next After #12");
+
+ test(new DfpDec(field, "1e-131092").nextAfter(ninf),
+ new DfpDec(field, "0"),
+ DfpField.FLAG_UNDERFLOW|DfpField.FLAG_INEXACT, "Next After #13");
+
+ test(new DfpDec(field, "9.9999999999999999e131078").nextAfter(pinf),
+ pinf,
+ DfpField.FLAG_OVERFLOW|DfpField.FLAG_INEXACT, "Next After #14");
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/dfp/DfpMathTest.java b/src/test/java/org/apache/commons/math/dfp/DfpMathTest.java
new file mode 100644
index 0000000..aba6fd5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/dfp/DfpMathTest.java
@@ -0,0 +1,587 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.dfp;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfpMathTest {
+
+ private DfpField factory;
+ private Dfp pinf;
+ private Dfp ninf;
+ private Dfp nan;
+ private Dfp qnan;
+
+ @Before
+ public void setUp() {
+ // Some basic setup. Define some constants and clear the status flags
+ factory = new DfpField(20);
+ pinf = factory.newDfp("1").divide(factory.newDfp("0"));
+ ninf = factory.newDfp("-1").divide(factory.newDfp("0"));
+ nan = factory.newDfp("0").divide(factory.newDfp("0"));
+ qnan = factory.newDfp((byte)1, Dfp.QNAN);
+ ninf.getField().clearIEEEFlags();
+
+ // force loading of dfpmath
+ Dfp pi = factory.getPi();
+ pi.getField().clearIEEEFlags();
+ }
+
+ @After
+ public void tearDown() {
+ pinf = null;
+ ninf = null;
+ nan = null;
+ qnan = null;
+ }
+
+ // Generic test function. Takes params x and y and tests them for
+ // equality. Then checks the status flags against the flags argument.
+ // If the test fail, it prints the desc string
+ private void test(Dfp x, Dfp y, int flags, String desc)
+ {
+ boolean b = x.equals(y);
+
+ if (!x.equals(y) && !x.unequal(y)) // NaNs involved
+ b = (x.toString().equals(y.toString()));
+
+ if (x.equals(factory.newDfp("0"))) // distinguish +/- zero
+ b = (b && (x.toString().equals(y.toString())));
+
+ b = (b && x.getField().getIEEEFlags() == flags);
+
+ if (!b)
+ Assert.assertTrue("assersion failed "+desc+" x = "+x.toString()+" flags = "+x.getField().getIEEEFlags(), b);
+
+ x.getField().clearIEEEFlags();
+ }
+
+ @Test
+ public void testPow()
+ {
+ // Test special cases exponent of zero
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("0")),
+ factory.newDfp("1"),
+ 0, "pow #1");
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("-0")),
+ factory.newDfp("1"),
+ 0, "pow #2");
+
+ test(DfpMath.pow(factory.newDfp("2"), factory.newDfp("0")),
+ factory.newDfp("1"),
+ 0, "pow #3");
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("-0")),
+ factory.newDfp("1"),
+ 0, "pow #4");
+
+ test(DfpMath.pow(pinf, factory.newDfp("-0")),
+ factory.newDfp("1"),
+ 0, "pow #5");
+
+ test(DfpMath.pow(pinf, factory.newDfp("0")),
+ factory.newDfp("1"),
+ 0, "pow #6");
+
+ test(DfpMath.pow(ninf, factory.newDfp("-0")),
+ factory.newDfp("1"),
+ 0, "pow #7");
+
+ test(DfpMath.pow(ninf, factory.newDfp("0")),
+ factory.newDfp("1"),
+ 0, "pow #8");
+
+ test(DfpMath.pow(qnan, factory.newDfp("0")),
+ factory.newDfp("1"),
+ 0, "pow #8");
+
+ // exponent of one
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("1")),
+ factory.newDfp("0"),
+ 0, "pow #9");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("1")),
+ factory.newDfp("-0"),
+ 0, "pow #10");
+
+ test(DfpMath.pow(factory.newDfp("2"), factory.newDfp("1")),
+ factory.newDfp("2"),
+ 0, "pow #11");
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("1")),
+ factory.newDfp("-2"),
+ 0, "pow #12");
+
+ test(DfpMath.pow(pinf, factory.newDfp("1")),
+ pinf,
+ 0, "pow #13");
+
+ test(DfpMath.pow(ninf, factory.newDfp("1")),
+ ninf,
+ 0, "pow #14");
+
+ test(DfpMath.pow(qnan, factory.newDfp("1")),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #14.1");
+
+ // exponent of NaN
+ test(DfpMath.pow(factory.newDfp("0"), qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #15");
+
+ test(DfpMath.pow(factory.newDfp("-0"), qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #16");
+
+ test(DfpMath.pow(factory.newDfp("2"), qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #17");
+
+ test(DfpMath.pow(factory.newDfp("-2"), qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #18");
+
+ test(DfpMath.pow(pinf, qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #19");
+
+ test(DfpMath.pow(ninf, qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #20");
+
+ test(DfpMath.pow(qnan, qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #21");
+
+ // radix of NaN
+ test(DfpMath.pow(qnan, factory.newDfp("1")),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #22");
+
+ test(DfpMath.pow(qnan, factory.newDfp("-1")),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #23");
+
+ test(DfpMath.pow(qnan, pinf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #24");
+
+ test(DfpMath.pow(qnan, ninf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #25");
+
+ test(DfpMath.pow(qnan, qnan),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #26");
+
+ // (x > 1) ^ pinf = pinf, (x < -1) ^ pinf = pinf
+ test(DfpMath.pow(factory.newDfp("2"), pinf),
+ pinf,
+ 0, "pow #27");
+
+ test(DfpMath.pow(factory.newDfp("-2"), pinf),
+ pinf,
+ 0, "pow #28");
+
+ test(DfpMath.pow(pinf, pinf),
+ pinf,
+ 0, "pow #29");
+
+ test(DfpMath.pow(ninf, pinf),
+ pinf,
+ 0, "pow #30");
+
+ // (x > 1) ^ ninf = +0, (x < -1) ^ ninf = +0
+ test(DfpMath.pow(factory.newDfp("2"), ninf),
+ factory.getZero(),
+ 0, "pow #31");
+
+ test(DfpMath.pow(factory.newDfp("-2"), ninf),
+ factory.getZero(),
+ 0, "pow #32");
+
+ test(DfpMath.pow(pinf, ninf),
+ factory.getZero(),
+ 0, "pow #33");
+
+ test(DfpMath.pow(ninf, ninf),
+ factory.getZero(),
+ 0, "pow #34");
+
+ // (-1 < x < 1) ^ pinf = 0
+ test(DfpMath.pow(factory.newDfp("0.5"), pinf),
+ factory.getZero(),
+ 0, "pow #35");
+
+ test(DfpMath.pow(factory.newDfp("-0.5"), pinf),
+ factory.getZero(),
+ 0, "pow #36");
+
+ // (-1 < x < 1) ^ ninf = pinf
+ test(DfpMath.pow(factory.newDfp("0.5"), ninf),
+ pinf,
+ 0, "pow #37");
+
+ test(DfpMath.pow(factory.newDfp("-0.5"), ninf),
+ pinf,
+ 0, "pow #38");
+
+ // +/- 1 ^ +/-inf = NaN
+ test(DfpMath.pow(factory.getOne(), pinf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #39");
+
+ test(DfpMath.pow(factory.getOne(), ninf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #40");
+
+ test(DfpMath.pow(factory.newDfp("-1"), pinf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #41");
+
+ test(DfpMath.pow(factory.getOne().negate(), ninf),
+ qnan,
+ DfpField.FLAG_INVALID, "pow #42");
+
+ // +0 ^ +anything except 0, NAN = +0
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("1")),
+ factory.newDfp("0"),
+ 0, "pow #43");
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("1e30")),
+ factory.newDfp("0"),
+ 0, "pow #44");
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("1e-30")),
+ factory.newDfp("0"),
+ 0, "pow #45");
+
+ test(DfpMath.pow(factory.newDfp("0"), pinf),
+ factory.newDfp("0"),
+ 0, "pow #46");
+
+ // -0 ^ +anything except 0, NAN, odd integer = +0
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("2")),
+ factory.newDfp("0"),
+ 0, "pow #47");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("1e30")),
+ factory.newDfp("0"),
+ 0, "pow #48");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("1e-30")),
+ factory.newDfp("0"),
+ DfpField.FLAG_INEXACT, "pow #49");
+
+ test(DfpMath.pow(factory.newDfp("-0"), pinf),
+ factory.newDfp("0"),
+ 0, "pow #50");
+
+ // +0 ^ -anything except 0, NAN = +INF
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("-1")),
+ pinf,
+ 0, "pow #51");
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("-1e30")),
+ pinf,
+ 0, "pow #52");
+
+ test(DfpMath.pow(factory.newDfp("0"), factory.newDfp("-1e-30")),
+ pinf,
+ 0, "pow #53");
+
+ test(DfpMath.pow(factory.newDfp("0"), ninf),
+ pinf,
+ 0, "pow #54");
+
+ // -0 ^ -anything except 0, NAN, odd integer = +INF
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("-2")),
+ pinf,
+ 0, "pow #55");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("-1e30")),
+ pinf,
+ 0, "pow #56");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("-1e-30")),
+ pinf,
+ DfpField.FLAG_INEXACT, "pow #57");
+
+ test(DfpMath.pow(factory.newDfp("-0"), ninf),
+ pinf,
+ 0, "pow #58");
+
+ // -0 ^ -odd integer = -INF
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("-1")),
+ ninf,
+ DfpField.FLAG_INEXACT, "pow #59");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("-12345")),
+ ninf,
+ DfpField.FLAG_INEXACT, "pow #60");
+
+ // -0 ^ +odd integer = -0
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("3")),
+ factory.newDfp("-0"),
+ DfpField.FLAG_INEXACT, "pow #61");
+
+ test(DfpMath.pow(factory.newDfp("-0"), factory.newDfp("12345")),
+ factory.newDfp("-0"),
+ DfpField.FLAG_INEXACT, "pow #62");
+
+ // pinf ^ +anything = pinf
+ test(DfpMath.pow(pinf, factory.newDfp("3")),
+ pinf,
+ 0, "pow #63");
+
+ test(DfpMath.pow(pinf, factory.newDfp("1e30")),
+ pinf,
+ 0, "pow #64");
+
+ test(DfpMath.pow(pinf, factory.newDfp("1e-30")),
+ pinf,
+ 0, "pow #65");
+
+ test(DfpMath.pow(pinf, pinf),
+ pinf,
+ 0, "pow #66");
+
+ // pinf ^ -anything = +0
+
+ test(DfpMath.pow(pinf, factory.newDfp("-3")),
+ factory.getZero(),
+ 0, "pow #67");
+
+ test(DfpMath.pow(pinf, factory.newDfp("-1e30")),
+ factory.getZero(),
+ 0, "pow #68");
+
+ test(DfpMath.pow(pinf, factory.newDfp("-1e-30")),
+ factory.getZero(),
+ 0, "pow #69");
+
+ test(DfpMath.pow(pinf, ninf),
+ factory.getZero(),
+ 0, "pow #70");
+
+ // ninf ^ anything = -0 ^ -anything
+ // ninf ^ -anything except 0, NAN, odd integer = +0
+
+ test(DfpMath.pow(ninf, factory.newDfp("-2")),
+ factory.newDfp("0"),
+ 0, "pow #71");
+
+ test(DfpMath.pow(ninf, factory.newDfp("-1e30")),
+ factory.newDfp("0"),
+ 0, "pow #72");
+
+ test(DfpMath.pow(ninf, factory.newDfp("-1e-30")),
+ factory.newDfp("0"),
+ DfpField.FLAG_INEXACT, "pow #73");
+
+ test(DfpMath.pow(ninf, ninf),
+ factory.newDfp("0"),
+ 0, "pow #74");
+
+ // ninf ^ +anything except 0, NAN, odd integer = +INF
+
+ test(DfpMath.pow(ninf, factory.newDfp("2")),
+ pinf,
+ 0, "pow #75");
+
+ test(DfpMath.pow(ninf, factory.newDfp("1e30")),
+ pinf,
+ 0, "pow #76");
+
+ test(DfpMath.pow(ninf, factory.newDfp("1e-30")),
+ pinf,
+ DfpField.FLAG_INEXACT, "pow #77");
+
+ test(DfpMath.pow(ninf, pinf),
+ pinf,
+ 0, "pow #78");
+
+ // ninf ^ +odd integer = -INF
+ test(DfpMath.pow(ninf, factory.newDfp("3")),
+ ninf,
+ DfpField.FLAG_INEXACT, "pow #79");
+
+ test(DfpMath.pow(ninf, factory.newDfp("12345")),
+ ninf,
+ DfpField.FLAG_INEXACT, "pow #80");
+
+ // ninf ^ -odd integer = -0
+ test(DfpMath.pow(ninf, factory.newDfp("-3")),
+ factory.newDfp("-0"),
+ DfpField.FLAG_INEXACT, "pow #81");
+
+ test(DfpMath.pow(ninf, factory.newDfp("-12345")),
+ factory.newDfp("-0"),
+ DfpField.FLAG_INEXACT, "pow #82");
+
+ // -anything ^ integer
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("3")),
+ factory.newDfp("-8"),
+ DfpField.FLAG_INEXACT, "pow #83");
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("16")),
+ factory.newDfp("65536"),
+ 0, "pow #84");
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("-3")),
+ factory.newDfp("-0.125"),
+ DfpField.FLAG_INEXACT, "pow #85");
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("-4")),
+ factory.newDfp("0.0625"),
+ 0, "pow #86");
+
+ // -anything ^ noninteger = NaN
+
+ test(DfpMath.pow(factory.newDfp("-2"), factory.newDfp("-4.1")),
+ qnan,
+ DfpField.FLAG_INVALID|DfpField.FLAG_INEXACT, "pow #87");
+
+ // Some fractional cases.
+ test(DfpMath.pow(factory.newDfp("2"),factory.newDfp("1.5")),
+ factory.newDfp("2.8284271247461901"),
+ DfpField.FLAG_INEXACT, "pow #88");
+ }
+
+ @Test
+ public void testSin()
+ {
+ test(DfpMath.sin(pinf),
+ nan,
+ DfpField.FLAG_INVALID|DfpField.FLAG_INEXACT, "sin #1");
+
+ test(DfpMath.sin(nan),
+ nan,
+ DfpField.FLAG_INVALID|DfpField.FLAG_INEXACT, "sin #2");
+
+ test(DfpMath.sin(factory.getZero()),
+ factory.getZero(),
+ DfpField.FLAG_INEXACT, "sin #3");
+
+ test(DfpMath.sin(factory.getPi()),
+ factory.getZero(),
+ DfpField.FLAG_INEXACT, "sin #4");
+
+ test(DfpMath.sin(factory.getPi().negate()),
+ factory.newDfp("-0"),
+ DfpField.FLAG_INEXACT, "sin #5");
+
+ test(DfpMath.sin(factory.getPi().multiply(2)),
+ factory.getZero(),
+ DfpField.FLAG_INEXACT, "sin #6");
+
+ test(DfpMath.sin(factory.getPi().divide(2)),
+ factory.getOne(),
+ DfpField.FLAG_INEXACT, "sin #7");
+
+ test(DfpMath.sin(factory.getPi().divide(2).negate()),
+ factory.getOne().negate(),
+ DfpField.FLAG_INEXACT, "sin #8");
+
+ test(DfpMath.sin(DfpMath.atan(factory.getOne())), // pi/4
+ factory.newDfp("0.5").sqrt(),
+ DfpField.FLAG_INEXACT, "sin #9");
+
+ test(DfpMath.sin(DfpMath.atan(factory.getOne())).negate(), // -pi/4
+ factory.newDfp("0.5").sqrt().negate(),
+ DfpField.FLAG_INEXACT, "sin #10");
+
+ test(DfpMath.sin(DfpMath.atan(factory.getOne())).negate(), // -pi/4
+ factory.newDfp("0.5").sqrt().negate(),
+ DfpField.FLAG_INEXACT, "sin #11");
+
+ test(DfpMath.sin(factory.newDfp("0.1")),
+ factory.newDfp("0.0998334166468281523"),
+ DfpField.FLAG_INEXACT, "sin #12");
+
+ test(DfpMath.sin(factory.newDfp("0.2")),
+ factory.newDfp("0.19866933079506121546"),
+ DfpField.FLAG_INEXACT, "sin #13");
+
+ test(DfpMath.sin(factory.newDfp("0.3")),
+ factory.newDfp("0.2955202066613395751"),
+ DfpField.FLAG_INEXACT, "sin #14");
+
+ test(DfpMath.sin(factory.newDfp("0.4")),
+ factory.newDfp("0.38941834230865049166"),
+ DfpField.FLAG_INEXACT, "sin #15");
+
+ test(DfpMath.sin(factory.newDfp("0.5")),
+ factory.newDfp("0.47942553860420300026"), // off by one ULP
+ DfpField.FLAG_INEXACT, "sin #16");
+
+ test(DfpMath.sin(factory.newDfp("0.6")),
+ factory.newDfp("0.56464247339503535721"), // off by one ULP
+ DfpField.FLAG_INEXACT, "sin #17");
+
+ test(DfpMath.sin(factory.newDfp("0.7")),
+ factory.newDfp("0.64421768723769105367"),
+ DfpField.FLAG_INEXACT, "sin #18");
+
+ test(DfpMath.sin(factory.newDfp("0.8")),
+ factory.newDfp("0.71735609089952276163"),
+ DfpField.FLAG_INEXACT, "sin #19");
+
+ test(DfpMath.sin(factory.newDfp("0.9")), // off by one ULP
+ factory.newDfp("0.78332690962748338847"),
+ DfpField.FLAG_INEXACT, "sin #20");
+
+ test(DfpMath.sin(factory.newDfp("1.0")),
+ factory.newDfp("0.84147098480789650666"),
+ DfpField.FLAG_INEXACT, "sin #21");
+
+ test(DfpMath.sin(factory.newDfp("1.1")),
+ factory.newDfp("0.89120736006143533995"),
+ DfpField.FLAG_INEXACT, "sin #22");
+
+ test(DfpMath.sin(factory.newDfp("1.2")),
+ factory.newDfp("0.93203908596722634968"),
+ DfpField.FLAG_INEXACT, "sin #23");
+
+ test(DfpMath.sin(factory.newDfp("1.3")),
+ factory.newDfp("0.9635581854171929647"),
+ DfpField.FLAG_INEXACT, "sin #24");
+
+ test(DfpMath.sin(factory.newDfp("1.4")),
+ factory.newDfp("0.98544972998846018066"),
+ DfpField.FLAG_INEXACT, "sin #25");
+
+ test(DfpMath.sin(factory.newDfp("1.5")),
+ factory.newDfp("0.99749498660405443096"),
+ DfpField.FLAG_INEXACT, "sin #26");
+
+ test(DfpMath.sin(factory.newDfp("1.6")),
+ factory.newDfp("0.99957360304150516323"),
+ DfpField.FLAG_INEXACT, "sin #27");
+ }
+
+
+}
diff --git a/src/test/java/org/apache/commons/math/dfp/DfpTest.java b/src/test/java/org/apache/commons/math/dfp/DfpTest.java
new file mode 100644
index 0000000..db5c05c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/dfp/DfpTest.java
@@ -0,0 +1,1507 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfpTest {
+
+ private DfpField field;
+ private Dfp pinf;
+ private Dfp ninf;
+ private Dfp nan;
+ private Dfp snan;
+ private Dfp qnan;
+
+ @Before
+ public void setUp() {
+ // Some basic setup. Define some constants and clear the status flags
+ field = new DfpField(20);
+ pinf = field.newDfp("1").divide(field.newDfp("0"));
+ ninf = field.newDfp("-1").divide(field.newDfp("0"));
+ nan = field.newDfp("0").divide(field.newDfp("0"));
+ snan = field.newDfp((byte)1, Dfp.SNAN);
+ qnan = field.newDfp((byte)1, Dfp.QNAN);
+ ninf.getField().clearIEEEFlags();
+ }
+
+ @After
+ public void tearDown() {
+ field = null;
+ pinf = null;
+ ninf = null;
+ nan = null;
+ snan = null;
+ qnan = null;
+ }
+
+ // Generic test function. Takes params x and y and tests them for
+ // equality. Then checks the status flags against the flags argument.
+ // If the test fail, it prints the desc string
+ private void test(Dfp x, Dfp y, int flags, String desc)
+ {
+ boolean b = x.equals(y);
+
+ if (!x.equals(y) && !x.unequal(y)) // NaNs involved
+ b = (x.toString().equals(y.toString()));
+
+ if (x.equals(field.newDfp("0"))) // distinguish +/- zero
+ b = (b && (x.toString().equals(y.toString())));
+
+ b = (b && x.getField().getIEEEFlags() == flags);
+
+ if (!b)
+ Assert.assertTrue("assersion failed "+desc+" x = "+x.toString()+" flags = "+x.getField().getIEEEFlags(), b);
+
+ x.getField().clearIEEEFlags();
+ }
+
+ @Test
+ public void testByteConstructor() {
+ Assert.assertEquals("0.", new Dfp(field, (byte) 0).toString());
+ Assert.assertEquals("1.", new Dfp(field, (byte) 1).toString());
+ Assert.assertEquals("-1.", new Dfp(field, (byte) -1).toString());
+ Assert.assertEquals("-128.", new Dfp(field, Byte.MIN_VALUE).toString());
+ Assert.assertEquals("127.", new Dfp(field, Byte.MAX_VALUE).toString());
+ }
+
+ @Test
+ public void testIntConstructor() {
+ Assert.assertEquals("0.", new Dfp(field, 0).toString());
+ Assert.assertEquals("1.", new Dfp(field, 1).toString());
+ Assert.assertEquals("-1.", new Dfp(field, -1).toString());
+ Assert.assertEquals("1234567890.", new Dfp(field, 1234567890).toString());
+ Assert.assertEquals("-1234567890.", new Dfp(field, -1234567890).toString());
+ Assert.assertEquals("-2147483648.", new Dfp(field, Integer.MIN_VALUE).toString());
+ Assert.assertEquals("2147483647.", new Dfp(field, Integer.MAX_VALUE).toString());
+ }
+
+ @Test
+ public void testLongConstructor() {
+ Assert.assertEquals("0.", new Dfp(field, 0l).toString());
+ Assert.assertEquals("1.", new Dfp(field, 1l).toString());
+ Assert.assertEquals("-1.", new Dfp(field, -1l).toString());
+ Assert.assertEquals("1234567890.", new Dfp(field, 1234567890l).toString());
+ Assert.assertEquals("-1234567890.", new Dfp(field, -1234567890l).toString());
+ Assert.assertEquals("-9223372036854775808.", new Dfp(field, Long.MIN_VALUE).toString());
+ Assert.assertEquals("9223372036854775807.", new Dfp(field, Long.MAX_VALUE).toString());
+ }
+
+ /*
+ * Test addition
+ */
+ @Test
+ public void testAdd()
+ {
+ test(field.newDfp("1").add(field.newDfp("1")), // Basic tests 1+1 = 2
+ field.newDfp("2"),
+ 0, "Add #1");
+
+ test(field.newDfp("1").add(field.newDfp("-1")), // 1 + (-1) = 0
+ field.newDfp("0"),
+ 0, "Add #2");
+
+ test(field.newDfp("-1").add(field.newDfp("1")), // (-1) + 1 = 0
+ field.newDfp("0"),
+ 0, "Add #3");
+
+ test(field.newDfp("-1").add(field.newDfp("-1")), // (-1) + (-1) = -2
+ field.newDfp("-2"),
+ 0, "Add #4");
+
+ // rounding mode is round half even
+
+ test(field.newDfp("1").add(field.newDfp("1e-16")), // rounding on add
+ field.newDfp("1.0000000000000001"),
+ 0, "Add #5");
+
+ test(field.newDfp("1").add(field.newDfp("1e-17")), // rounding on add
+ field.newDfp("1"),
+ DfpField.FLAG_INEXACT, "Add #6");
+
+ test(field.newDfp("0.90999999999999999999").add(field.newDfp("0.1")), // rounding on add
+ field.newDfp("1.01"),
+ DfpField.FLAG_INEXACT, "Add #7");
+
+ test(field.newDfp(".10000000000000005000").add(field.newDfp(".9")), // rounding on add
+ field.newDfp("1."),
+ DfpField.FLAG_INEXACT, "Add #8");
+
+ test(field.newDfp(".10000000000000015000").add(field.newDfp(".9")), // rounding on add
+ field.newDfp("1.0000000000000002"),
+ DfpField.FLAG_INEXACT, "Add #9");
+
+ test(field.newDfp(".10000000000000014999").add(field.newDfp(".9")), // rounding on add
+ field.newDfp("1.0000000000000001"),
+ DfpField.FLAG_INEXACT, "Add #10");
+
+ test(field.newDfp(".10000000000000015001").add(field.newDfp(".9")), // rounding on add
+ field.newDfp("1.0000000000000002"),
+ DfpField.FLAG_INEXACT, "Add #11");
+
+ test(field.newDfp(".11111111111111111111").add(field.newDfp("11.1111111111111111")), // rounding on add
+ field.newDfp("11.22222222222222222222"),
+ DfpField.FLAG_INEXACT, "Add #12");
+
+ test(field.newDfp(".11111111111111111111").add(field.newDfp("1111111111111111.1111")), // rounding on add
+ field.newDfp("1111111111111111.2222"),
+ DfpField.FLAG_INEXACT, "Add #13");
+
+ test(field.newDfp(".11111111111111111111").add(field.newDfp("11111111111111111111")), // rounding on add
+ field.newDfp("11111111111111111111"),
+ DfpField.FLAG_INEXACT, "Add #14");
+
+ test(field.newDfp("9.9999999999999999999e131071").add(field.newDfp("-1e131052")), // overflow on add
+ field.newDfp("9.9999999999999999998e131071"),
+ 0, "Add #15");
+
+ test(field.newDfp("9.9999999999999999999e131071").add(field.newDfp("1e131052")), // overflow on add
+ pinf,
+ DfpField.FLAG_OVERFLOW, "Add #16");
+
+ test(field.newDfp("-9.9999999999999999999e131071").add(field.newDfp("-1e131052")), // overflow on add
+ ninf,
+ DfpField.FLAG_OVERFLOW, "Add #17");
+
+ test(field.newDfp("-9.9999999999999999999e131071").add(field.newDfp("1e131052")), // overflow on add
+ field.newDfp("-9.9999999999999999998e131071"),
+ 0, "Add #18");
+
+ test(field.newDfp("1e-131072").add(field.newDfp("1e-131072")), // underflow on add
+ field.newDfp("2e-131072"),
+ 0, "Add #19");
+
+ test(field.newDfp("1.0000000000000001e-131057").add(field.newDfp("-1e-131057")), // underflow on add
+ field.newDfp("1e-131073"),
+ DfpField.FLAG_UNDERFLOW, "Add #20");
+
+ test(field.newDfp("1.1e-131072").add(field.newDfp("-1e-131072")), // underflow on add
+ field.newDfp("1e-131073"),
+ DfpField.FLAG_UNDERFLOW, "Add #21");
+
+ test(field.newDfp("1.0000000000000001e-131072").add(field.newDfp("-1e-131072")), // underflow on add
+ field.newDfp("1e-131088"),
+ DfpField.FLAG_UNDERFLOW, "Add #22");
+
+ test(field.newDfp("1.0000000000000001e-131078").add(field.newDfp("-1e-131078")), // underflow on add
+ field.newDfp("0"),
+ DfpField.FLAG_UNDERFLOW, "Add #23");
+
+ test(field.newDfp("1.0").add(field.newDfp("-1e-20")), // loss of precision on alignment?
+ field.newDfp("0.99999999999999999999"),
+ 0, "Add #23.1");
+
+ test(field.newDfp("-0.99999999999999999999").add(field.newDfp("1")), // proper normalization?
+ field.newDfp("0.00000000000000000001"),
+ 0, "Add #23.2");
+
+ test(field.newDfp("1").add(field.newDfp("0")), // adding zeros
+ field.newDfp("1"),
+ 0, "Add #24");
+
+ test(field.newDfp("0").add(field.newDfp("0")), // adding zeros
+ field.newDfp("0"),
+ 0, "Add #25");
+
+ test(field.newDfp("-0").add(field.newDfp("0")), // adding zeros
+ field.newDfp("0"),
+ 0, "Add #26");
+
+ test(field.newDfp("0").add(field.newDfp("-0")), // adding zeros
+ field.newDfp("0"),
+ 0, "Add #27");
+
+ test(field.newDfp("-0").add(field.newDfp("-0")), // adding zeros
+ field.newDfp("-0"),
+ 0, "Add #28");
+
+ test(field.newDfp("1e-20").add(field.newDfp("0")), // adding zeros
+ field.newDfp("1e-20"),
+ 0, "Add #29");
+
+ test(field.newDfp("1e-40").add(field.newDfp("0")), // adding zeros
+ field.newDfp("1e-40"),
+ 0, "Add #30");
+
+ test(pinf.add(ninf), // adding infinities
+ nan,
+ DfpField.FLAG_INVALID, "Add #31");
+
+ test(ninf.add(pinf), // adding infinities
+ nan,
+ DfpField.FLAG_INVALID, "Add #32");
+
+ test(ninf.add(ninf), // adding infinities
+ ninf,
+ 0, "Add #33");
+
+ test(pinf.add(pinf), // adding infinities
+ pinf,
+ 0, "Add #34");
+
+ test(pinf.add(field.newDfp("0")), // adding infinities
+ pinf,
+ 0, "Add #35");
+
+ test(pinf.add(field.newDfp("-1e131071")), // adding infinities
+ pinf,
+ 0, "Add #36");
+
+ test(pinf.add(field.newDfp("1e131071")), // adding infinities
+ pinf,
+ 0, "Add #37");
+
+ test(field.newDfp("0").add(pinf), // adding infinities
+ pinf,
+ 0, "Add #38");
+
+ test(field.newDfp("-1e131071").add(pinf), // adding infinities
+ pinf,
+ 0, "Add #39");
+
+ test(field.newDfp("1e131071").add(pinf), // adding infinities
+ pinf,
+ 0, "Add #40");
+
+ test(ninf.add(field.newDfp("0")), // adding infinities
+ ninf,
+ 0, "Add #41");
+
+ test(ninf.add(field.newDfp("-1e131071")), // adding infinities
+ ninf,
+ 0, "Add #42");
+
+ test(ninf.add(field.newDfp("1e131071")), // adding infinities
+ ninf,
+ 0, "Add #43");
+
+ test(field.newDfp("0").add(ninf), // adding infinities
+ ninf,
+ 0, "Add #44");
+
+ test(field.newDfp("-1e131071").add(ninf), // adding infinities
+ ninf,
+ 0, "Add #45");
+
+ test(field.newDfp("1e131071").add(ninf), // adding infinities
+ ninf,
+ 0, "Add #46");
+
+ test(field.newDfp("9.9999999999999999999e131071").add(field.newDfp("5e131051")), // overflow
+ pinf,
+ DfpField.FLAG_OVERFLOW, "Add #47");
+
+ test(field.newDfp("9.9999999999999999999e131071").add(field.newDfp("4.9999999999999999999e131051")), // overflow
+ field.newDfp("9.9999999999999999999e131071"),
+ DfpField.FLAG_INEXACT, "Add #48");
+
+ test(nan.add(field.newDfp("1")),
+ nan,
+ 0, "Add #49");
+
+ test(field.newDfp("1").add(nan),
+ nan,
+ 0, "Add #50");
+
+ test(field.newDfp("12345678123456781234").add(field.newDfp("0.12345678123456781234")),
+ field.newDfp("12345678123456781234"),
+ DfpField.FLAG_INEXACT, "Add #51");
+
+ test(field.newDfp("12345678123456781234").add(field.newDfp("123.45678123456781234")),
+ field.newDfp("12345678123456781357"),
+ DfpField.FLAG_INEXACT, "Add #52");
+
+ test(field.newDfp("123.45678123456781234").add(field.newDfp("12345678123456781234")),
+ field.newDfp("12345678123456781357"),
+ DfpField.FLAG_INEXACT, "Add #53");
+
+ test(field.newDfp("12345678123456781234").add(field.newDfp(".00001234567812345678")),
+ field.newDfp("12345678123456781234"),
+ DfpField.FLAG_INEXACT, "Add #54");
+
+ test(field.newDfp("12345678123456781234").add(field.newDfp(".00000000123456781234")),
+ field.newDfp("12345678123456781234"),
+ DfpField.FLAG_INEXACT, "Add #55");
+
+ test(field.newDfp("-0").add(field.newDfp("-0")),
+ field.newDfp("-0"),
+ 0, "Add #56");
+
+ test(field.newDfp("0").add(field.newDfp("-0")),
+ field.newDfp("0"),
+ 0, "Add #57");
+
+ test(field.newDfp("-0").add(field.newDfp("0")),
+ field.newDfp("0"),
+ 0, "Add #58");
+
+ test(field.newDfp("0").add(field.newDfp("0")),
+ field.newDfp("0"),
+ 0, "Add #59");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ // Test comparisons
+
+ // utility function to help test comparisons
+ private void cmptst(Dfp a, Dfp b, String op, boolean result, double num)
+ {
+ if (op == "equal")
+ if (a.equals(b) != result)
+ Assert.fail("assersion failed. "+op+" compare #"+num);
+
+ if (op == "unequal")
+ if (a.unequal(b) != result)
+ Assert.fail("assersion failed. "+op+" compare #"+num);
+
+ if (op == "lessThan")
+ if (a.lessThan(b) != result)
+ Assert.fail("assersion failed. "+op+" compare #"+num);
+
+ if (op == "greaterThan")
+ if (a.greaterThan(b) != result)
+ Assert.fail("assersion failed. "+op+" compare #"+num);
+ }
+
+ @Test
+ public void testCompare()
+ {
+ // test equal() comparison
+ // check zero vs. zero
+ field.clearIEEEFlags();
+
+ cmptst(field.newDfp("0"), field.newDfp("0"), "equal", true, 1); // 0 == 0
+ cmptst(field.newDfp("0"), field.newDfp("-0"), "equal", true, 2); // 0 == -0
+ cmptst(field.newDfp("-0"), field.newDfp("-0"), "equal", true, 3); // -0 == -0
+ cmptst(field.newDfp("-0"), field.newDfp("0"), "equal", true, 4); // -0 == 0
+
+ // check zero vs normal numbers
+
+ cmptst(field.newDfp("0"), field.newDfp("1"), "equal", false, 5); // 0 == 1
+ cmptst(field.newDfp("1"), field.newDfp("0"), "equal", false, 6); // 1 == 0
+ cmptst(field.newDfp("-1"), field.newDfp("0"), "equal", false, 7); // -1 == 0
+ cmptst(field.newDfp("0"), field.newDfp("-1"), "equal", false, 8); // 0 == -1
+ cmptst(field.newDfp("0"), field.newDfp("1e-131072"), "equal", false, 9); // 0 == 1e-131072
+ // check flags
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0"), field.newDfp("1e-131078"), "equal", false, 10); // 0 == 1e-131078
+
+ // check flags -- underflow should be set
+ if (field.getIEEEFlags() != DfpField.FLAG_UNDERFLOW)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ field.clearIEEEFlags();
+
+ cmptst(field.newDfp("0"), field.newDfp("1e+131071"), "equal", false, 11); // 0 == 1e+131071
+
+ // check zero vs infinities
+
+ cmptst(field.newDfp("0"), pinf, "equal", false, 12); // 0 == pinf
+ cmptst(field.newDfp("0"), ninf, "equal", false, 13); // 0 == ninf
+ cmptst(field.newDfp("-0"), pinf, "equal", false, 14); // -0 == pinf
+ cmptst(field.newDfp("-0"), ninf, "equal", false, 15); // -0 == ninf
+ cmptst(pinf, field.newDfp("0"), "equal", false, 16); // pinf == 0
+ cmptst(ninf, field.newDfp("0"), "equal", false, 17); // ninf == 0
+ cmptst(pinf, field.newDfp("-0"), "equal", false, 18); // pinf == -0
+ cmptst(ninf, field.newDfp("-0"), "equal", false, 19); // ninf == -0
+ cmptst(ninf, pinf, "equal", false, 19.10); // ninf == pinf
+ cmptst(pinf, ninf, "equal", false, 19.11); // pinf == ninf
+ cmptst(pinf, pinf, "equal", true, 19.12); // pinf == pinf
+ cmptst(ninf, ninf, "equal", true, 19.13); // ninf == ninf
+
+ // check some normal numbers
+ cmptst(field.newDfp("1"), field.newDfp("1"), "equal", true, 20); // 1 == 1
+ cmptst(field.newDfp("1"), field.newDfp("-1"), "equal", false, 21); // 1 == -1
+ cmptst(field.newDfp("-1"), field.newDfp("-1"), "equal", true, 22); // -1 == -1
+ cmptst(field.newDfp("1"), field.newDfp("1.0000000000000001"), "equal", false, 23); // 1 == 1.0000000000000001
+
+ // The tests below checks to ensure that comparisons don't set FLAG_INEXACT
+ // 100000 == 1.0000000000000001
+ cmptst(field.newDfp("1e20"), field.newDfp("1.0000000000000001"), "equal", false, 24);
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0.000001"), field.newDfp("1e-6"), "equal", true, 25);
+
+ // check some nans -- nans shouldnt equal anything
+
+ cmptst(snan, snan, "equal", false, 27);
+ cmptst(qnan, qnan, "equal", false, 28);
+ cmptst(snan, qnan, "equal", false, 29);
+ cmptst(qnan, snan, "equal", false, 30);
+ cmptst(qnan, field.newDfp("0"), "equal", false, 31);
+ cmptst(snan, field.newDfp("0"), "equal", false, 32);
+ cmptst(field.newDfp("0"), snan, "equal", false, 33);
+ cmptst(field.newDfp("0"), qnan, "equal", false, 34);
+ cmptst(qnan, pinf, "equal", false, 35);
+ cmptst(snan, pinf, "equal", false, 36);
+ cmptst(pinf, snan, "equal", false, 37);
+ cmptst(pinf, qnan, "equal", false, 38);
+ cmptst(qnan, ninf, "equal", false, 39);
+ cmptst(snan, ninf, "equal", false, 40);
+ cmptst(ninf, snan, "equal", false, 41);
+ cmptst(ninf, qnan, "equal", false, 42);
+ cmptst(qnan, field.newDfp("-1"), "equal", false, 43);
+ cmptst(snan, field.newDfp("-1"), "equal", false, 44);
+ cmptst(field.newDfp("-1"), snan, "equal", false, 45);
+ cmptst(field.newDfp("-1"), qnan, "equal", false, 46);
+ cmptst(qnan, field.newDfp("1"), "equal", false, 47);
+ cmptst(snan, field.newDfp("1"), "equal", false, 48);
+ cmptst(field.newDfp("1"), snan, "equal", false, 49);
+ cmptst(field.newDfp("1"), qnan, "equal", false, 50);
+ cmptst(snan.negate(), snan, "equal", false, 51);
+ cmptst(qnan.negate(), qnan, "equal", false, 52);
+
+ //
+ // Tests for un equal -- do it all over again
+ //
+
+ cmptst(field.newDfp("0"), field.newDfp("0"), "unequal", false, 1); // 0 == 0
+ cmptst(field.newDfp("0"), field.newDfp("-0"), "unequal", false, 2); // 0 == -0
+ cmptst(field.newDfp("-0"), field.newDfp("-0"), "unequal", false, 3); // -0 == -0
+ cmptst(field.newDfp("-0"), field.newDfp("0"), "unequal", false, 4); // -0 == 0
+
+ // check zero vs normal numbers
+
+ cmptst(field.newDfp("0"), field.newDfp("1"), "unequal", true, 5); // 0 == 1
+ cmptst(field.newDfp("1"), field.newDfp("0"), "unequal", true, 6); // 1 == 0
+ cmptst(field.newDfp("-1"), field.newDfp("0"), "unequal", true, 7); // -1 == 0
+ cmptst(field.newDfp("0"), field.newDfp("-1"), "unequal", true, 8); // 0 == -1
+ cmptst(field.newDfp("0"), field.newDfp("1e-131072"), "unequal", true, 9); // 0 == 1e-131072
+ // check flags
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0"), field.newDfp("1e-131078"), "unequal", true, 10); // 0 == 1e-131078
+
+ // check flags -- underflow should be set
+ if (field.getIEEEFlags() != DfpField.FLAG_UNDERFLOW)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ field.clearIEEEFlags();
+
+ cmptst(field.newDfp("0"), field.newDfp("1e+131071"), "unequal", true, 11); // 0 == 1e+131071
+
+ // check zero vs infinities
+
+ cmptst(field.newDfp("0"), pinf, "unequal", true, 12); // 0 == pinf
+ cmptst(field.newDfp("0"), ninf, "unequal", true, 13); // 0 == ninf
+ cmptst(field.newDfp("-0"), pinf, "unequal", true, 14); // -0 == pinf
+ cmptst(field.newDfp("-0"), ninf, "unequal", true, 15); // -0 == ninf
+ cmptst(pinf, field.newDfp("0"), "unequal", true, 16); // pinf == 0
+ cmptst(ninf, field.newDfp("0"), "unequal", true, 17); // ninf == 0
+ cmptst(pinf, field.newDfp("-0"), "unequal", true, 18); // pinf == -0
+ cmptst(ninf, field.newDfp("-0"), "unequal", true, 19); // ninf == -0
+ cmptst(ninf, pinf, "unequal", true, 19.10); // ninf == pinf
+ cmptst(pinf, ninf, "unequal", true, 19.11); // pinf == ninf
+ cmptst(pinf, pinf, "unequal", false, 19.12); // pinf == pinf
+ cmptst(ninf, ninf, "unequal", false, 19.13); // ninf == ninf
+
+ // check some normal numbers
+ cmptst(field.newDfp("1"), field.newDfp("1"), "unequal", false, 20); // 1 == 1
+ cmptst(field.newDfp("1"), field.newDfp("-1"), "unequal", true, 21); // 1 == -1
+ cmptst(field.newDfp("-1"), field.newDfp("-1"), "unequal", false, 22); // -1 == -1
+ cmptst(field.newDfp("1"), field.newDfp("1.0000000000000001"), "unequal", true, 23); // 1 == 1.0000000000000001
+
+ // The tests below checks to ensure that comparisons don't set FLAG_INEXACT
+ // 100000 == 1.0000000000000001
+ cmptst(field.newDfp("1e20"), field.newDfp("1.0000000000000001"), "unequal", true, 24);
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0.000001"), field.newDfp("1e-6"), "unequal", false, 25);
+
+ // check some nans -- nans shouldnt be unequal to anything
+
+ cmptst(snan, snan, "unequal", false, 27);
+ cmptst(qnan, qnan, "unequal", false, 28);
+ cmptst(snan, qnan, "unequal", false, 29);
+ cmptst(qnan, snan, "unequal", false, 30);
+ cmptst(qnan, field.newDfp("0"), "unequal", false, 31);
+ cmptst(snan, field.newDfp("0"), "unequal", false, 32);
+ cmptst(field.newDfp("0"), snan, "unequal", false, 33);
+ cmptst(field.newDfp("0"), qnan, "unequal", false, 34);
+ cmptst(qnan, pinf, "unequal", false, 35);
+ cmptst(snan, pinf, "unequal", false, 36);
+ cmptst(pinf, snan, "unequal", false, 37);
+ cmptst(pinf, qnan, "unequal", false, 38);
+ cmptst(qnan, ninf, "unequal", false, 39);
+ cmptst(snan, ninf, "unequal", false, 40);
+ cmptst(ninf, snan, "unequal", false, 41);
+ cmptst(ninf, qnan, "unequal", false, 42);
+ cmptst(qnan, field.newDfp("-1"), "unequal", false, 43);
+ cmptst(snan, field.newDfp("-1"), "unequal", false, 44);
+ cmptst(field.newDfp("-1"), snan, "unequal", false, 45);
+ cmptst(field.newDfp("-1"), qnan, "unequal", false, 46);
+ cmptst(qnan, field.newDfp("1"), "unequal", false, 47);
+ cmptst(snan, field.newDfp("1"), "unequal", false, 48);
+ cmptst(field.newDfp("1"), snan, "unequal", false, 49);
+ cmptst(field.newDfp("1"), qnan, "unequal", false, 50);
+ cmptst(snan.negate(), snan, "unequal", false, 51);
+ cmptst(qnan.negate(), qnan, "unequal", false, 52);
+
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare unequal flags = "+field.getIEEEFlags());
+
+ //
+ // Tests for lessThan -- do it all over again
+ //
+
+ cmptst(field.newDfp("0"), field.newDfp("0"), "lessThan", false, 1); // 0 < 0
+ cmptst(field.newDfp("0"), field.newDfp("-0"), "lessThan", false, 2); // 0 < -0
+ cmptst(field.newDfp("-0"), field.newDfp("-0"), "lessThan", false, 3); // -0 < -0
+ cmptst(field.newDfp("-0"), field.newDfp("0"), "lessThan", false, 4); // -0 < 0
+
+ // check zero vs normal numbers
+
+ cmptst(field.newDfp("0"), field.newDfp("1"), "lessThan", true, 5); // 0 < 1
+ cmptst(field.newDfp("1"), field.newDfp("0"), "lessThan", false, 6); // 1 < 0
+ cmptst(field.newDfp("-1"), field.newDfp("0"), "lessThan", true, 7); // -1 < 0
+ cmptst(field.newDfp("0"), field.newDfp("-1"), "lessThan", false, 8); // 0 < -1
+ cmptst(field.newDfp("0"), field.newDfp("1e-131072"), "lessThan", true, 9); // 0 < 1e-131072
+ // check flags
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0"), field.newDfp("1e-131078"), "lessThan", true, 10); // 0 < 1e-131078
+
+ // check flags -- underflow should be set
+ if (field.getIEEEFlags() != DfpField.FLAG_UNDERFLOW)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+ field.clearIEEEFlags();
+
+ cmptst(field.newDfp("0"), field.newDfp("1e+131071"), "lessThan", true, 11); // 0 < 1e+131071
+
+ // check zero vs infinities
+
+ cmptst(field.newDfp("0"), pinf, "lessThan", true, 12); // 0 < pinf
+ cmptst(field.newDfp("0"), ninf, "lessThan", false, 13); // 0 < ninf
+ cmptst(field.newDfp("-0"), pinf, "lessThan", true, 14); // -0 < pinf
+ cmptst(field.newDfp("-0"), ninf, "lessThan", false, 15); // -0 < ninf
+ cmptst(pinf, field.newDfp("0"), "lessThan", false, 16); // pinf < 0
+ cmptst(ninf, field.newDfp("0"), "lessThan", true, 17); // ninf < 0
+ cmptst(pinf, field.newDfp("-0"), "lessThan", false, 18); // pinf < -0
+ cmptst(ninf, field.newDfp("-0"), "lessThan", true, 19); // ninf < -0
+ cmptst(ninf, pinf, "lessThan", true, 19.10); // ninf < pinf
+ cmptst(pinf, ninf, "lessThan", false, 19.11); // pinf < ninf
+ cmptst(pinf, pinf, "lessThan", false, 19.12); // pinf < pinf
+ cmptst(ninf, ninf, "lessThan", false, 19.13); // ninf < ninf
+
+ // check some normal numbers
+ cmptst(field.newDfp("1"), field.newDfp("1"), "lessThan", false, 20); // 1 < 1
+ cmptst(field.newDfp("1"), field.newDfp("-1"), "lessThan", false, 21); // 1 < -1
+ cmptst(field.newDfp("-1"), field.newDfp("-1"), "lessThan", false, 22); // -1 < -1
+ cmptst(field.newDfp("1"), field.newDfp("1.0000000000000001"), "lessThan", true, 23); // 1 < 1.0000000000000001
+
+ // The tests below checks to ensure that comparisons don't set FLAG_INEXACT
+ // 100000 < 1.0000000000000001
+ cmptst(field.newDfp("1e20"), field.newDfp("1.0000000000000001"), "lessThan", false, 24);
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0.000001"), field.newDfp("1e-6"), "lessThan", false, 25);
+
+ // check some nans -- nans shouldnt be lessThan to anything
+ cmptst(snan, snan, "lessThan", false, 27);
+ cmptst(qnan, qnan, "lessThan", false, 28);
+ cmptst(snan, qnan, "lessThan", false, 29);
+ cmptst(qnan, snan, "lessThan", false, 30);
+ cmptst(qnan, field.newDfp("0"), "lessThan", false, 31);
+ cmptst(snan, field.newDfp("0"), "lessThan", false, 32);
+ cmptst(field.newDfp("0"), snan, "lessThan", false, 33);
+ cmptst(field.newDfp("0"), qnan, "lessThan", false, 34);
+ cmptst(qnan, pinf, "lessThan", false, 35);
+ cmptst(snan, pinf, "lessThan", false, 36);
+ cmptst(pinf, snan, "lessThan", false, 37);
+ cmptst(pinf, qnan, "lessThan", false, 38);
+ cmptst(qnan, ninf, "lessThan", false, 39);
+ cmptst(snan, ninf, "lessThan", false, 40);
+ cmptst(ninf, snan, "lessThan", false, 41);
+ cmptst(ninf, qnan, "lessThan", false, 42);
+ cmptst(qnan, field.newDfp("-1"), "lessThan", false, 43);
+ cmptst(snan, field.newDfp("-1"), "lessThan", false, 44);
+ cmptst(field.newDfp("-1"), snan, "lessThan", false, 45);
+ cmptst(field.newDfp("-1"), qnan, "lessThan", false, 46);
+ cmptst(qnan, field.newDfp("1"), "lessThan", false, 47);
+ cmptst(snan, field.newDfp("1"), "lessThan", false, 48);
+ cmptst(field.newDfp("1"), snan, "lessThan", false, 49);
+ cmptst(field.newDfp("1"), qnan, "lessThan", false, 50);
+ cmptst(snan.negate(), snan, "lessThan", false, 51);
+ cmptst(qnan.negate(), qnan, "lessThan", false, 52);
+
+ //lessThan compares with nans should raise FLAG_INVALID
+ if (field.getIEEEFlags() != DfpField.FLAG_INVALID)
+ Assert.fail("assersion failed. compare lessThan flags = "+field.getIEEEFlags());
+ field.clearIEEEFlags();
+
+ //
+ // Tests for greaterThan -- do it all over again
+ //
+
+ cmptst(field.newDfp("0"), field.newDfp("0"), "greaterThan", false, 1); // 0 > 0
+ cmptst(field.newDfp("0"), field.newDfp("-0"), "greaterThan", false, 2); // 0 > -0
+ cmptst(field.newDfp("-0"), field.newDfp("-0"), "greaterThan", false, 3); // -0 > -0
+ cmptst(field.newDfp("-0"), field.newDfp("0"), "greaterThan", false, 4); // -0 > 0
+
+ // check zero vs normal numbers
+
+ cmptst(field.newDfp("0"), field.newDfp("1"), "greaterThan", false, 5); // 0 > 1
+ cmptst(field.newDfp("1"), field.newDfp("0"), "greaterThan", true, 6); // 1 > 0
+ cmptst(field.newDfp("-1"), field.newDfp("0"), "greaterThan", false, 7); // -1 > 0
+ cmptst(field.newDfp("0"), field.newDfp("-1"), "greaterThan", true, 8); // 0 > -1
+ cmptst(field.newDfp("0"), field.newDfp("1e-131072"), "greaterThan", false, 9); // 0 > 1e-131072
+ // check flags
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0"), field.newDfp("1e-131078"), "greaterThan", false, 10); // 0 > 1e-131078
+
+ // check flags -- underflow should be set
+ if (field.getIEEEFlags() != DfpField.FLAG_UNDERFLOW)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+ field.clearIEEEFlags();
+
+ cmptst(field.newDfp("0"), field.newDfp("1e+131071"), "greaterThan", false, 11); // 0 > 1e+131071
+
+ // check zero vs infinities
+
+ cmptst(field.newDfp("0"), pinf, "greaterThan", false, 12); // 0 > pinf
+ cmptst(field.newDfp("0"), ninf, "greaterThan", true, 13); // 0 > ninf
+ cmptst(field.newDfp("-0"), pinf, "greaterThan", false, 14); // -0 > pinf
+ cmptst(field.newDfp("-0"), ninf, "greaterThan", true, 15); // -0 > ninf
+ cmptst(pinf, field.newDfp("0"), "greaterThan", true, 16); // pinf > 0
+ cmptst(ninf, field.newDfp("0"), "greaterThan", false, 17); // ninf > 0
+ cmptst(pinf, field.newDfp("-0"), "greaterThan", true, 18); // pinf > -0
+ cmptst(ninf, field.newDfp("-0"), "greaterThan", false, 19); // ninf > -0
+ cmptst(ninf, pinf, "greaterThan", false, 19.10); // ninf > pinf
+ cmptst(pinf, ninf, "greaterThan", true, 19.11); // pinf > ninf
+ cmptst(pinf, pinf, "greaterThan", false, 19.12); // pinf > pinf
+ cmptst(ninf, ninf, "greaterThan", false, 19.13); // ninf > ninf
+
+ // check some normal numbers
+ cmptst(field.newDfp("1"), field.newDfp("1"), "greaterThan", false, 20); // 1 > 1
+ cmptst(field.newDfp("1"), field.newDfp("-1"), "greaterThan", true, 21); // 1 > -1
+ cmptst(field.newDfp("-1"), field.newDfp("-1"), "greaterThan", false, 22); // -1 > -1
+ cmptst(field.newDfp("1"), field.newDfp("1.0000000000000001"), "greaterThan", false, 23); // 1 > 1.0000000000000001
+
+ // The tests below checks to ensure that comparisons don't set FLAG_INEXACT
+ // 100000 > 1.0000000000000001
+ cmptst(field.newDfp("1e20"), field.newDfp("1.0000000000000001"), "greaterThan", true, 24);
+ if (field.getIEEEFlags() != 0)
+ Assert.fail("assersion failed. compare flags = "+field.getIEEEFlags());
+
+ cmptst(field.newDfp("0.000001"), field.newDfp("1e-6"), "greaterThan", false, 25);
+
+ // check some nans -- nans shouldnt be greaterThan to anything
+ cmptst(snan, snan, "greaterThan", false, 27);
+ cmptst(qnan, qnan, "greaterThan", false, 28);
+ cmptst(snan, qnan, "greaterThan", false, 29);
+ cmptst(qnan, snan, "greaterThan", false, 30);
+ cmptst(qnan, field.newDfp("0"), "greaterThan", false, 31);
+ cmptst(snan, field.newDfp("0"), "greaterThan", false, 32);
+ cmptst(field.newDfp("0"), snan, "greaterThan", false, 33);
+ cmptst(field.newDfp("0"), qnan, "greaterThan", false, 34);
+ cmptst(qnan, pinf, "greaterThan", false, 35);
+ cmptst(snan, pinf, "greaterThan", false, 36);
+ cmptst(pinf, snan, "greaterThan", false, 37);
+ cmptst(pinf, qnan, "greaterThan", false, 38);
+ cmptst(qnan, ninf, "greaterThan", false, 39);
+ cmptst(snan, ninf, "greaterThan", false, 40);
+ cmptst(ninf, snan, "greaterThan", false, 41);
+ cmptst(ninf, qnan, "greaterThan", false, 42);
+ cmptst(qnan, field.newDfp("-1"), "greaterThan", false, 43);
+ cmptst(snan, field.newDfp("-1"), "greaterThan", false, 44);
+ cmptst(field.newDfp("-1"), snan, "greaterThan", false, 45);
+ cmptst(field.newDfp("-1"), qnan, "greaterThan", false, 46);
+ cmptst(qnan, field.newDfp("1"), "greaterThan", false, 47);
+ cmptst(snan, field.newDfp("1"), "greaterThan", false, 48);
+ cmptst(field.newDfp("1"), snan, "greaterThan", false, 49);
+ cmptst(field.newDfp("1"), qnan, "greaterThan", false, 50);
+ cmptst(snan.negate(), snan, "greaterThan", false, 51);
+ cmptst(qnan.negate(), qnan, "greaterThan", false, 52);
+
+ //greaterThan compares with nans should raise FLAG_INVALID
+ if (field.getIEEEFlags() != DfpField.FLAG_INVALID)
+ Assert.fail("assersion failed. compare greaterThan flags = "+field.getIEEEFlags());
+ field.clearIEEEFlags();
+ }
+
+ //
+ // Test multiplication
+ //
+ @Test
+ public void testMultiply()
+ {
+ test(field.newDfp("1").multiply(field.newDfp("1")), // Basic tests 1*1 = 1
+ field.newDfp("1"),
+ 0, "Multiply #1");
+
+ test(field.newDfp("1").multiply(1), // Basic tests 1*1 = 1
+ field.newDfp("1"),
+ 0, "Multiply #2");
+
+ test(field.newDfp("-1").multiply(field.newDfp("1")), // Basic tests -1*1 = -1
+ field.newDfp("-1"),
+ 0, "Multiply #3");
+
+ test(field.newDfp("-1").multiply(1), // Basic tests -1*1 = -1
+ field.newDfp("-1"),
+ 0, "Multiply #4");
+
+ // basic tests with integers
+ test(field.newDfp("2").multiply(field.newDfp("3")),
+ field.newDfp("6"),
+ 0, "Multiply #5");
+
+ test(field.newDfp("2").multiply(3),
+ field.newDfp("6"),
+ 0, "Multiply #6");
+
+ test(field.newDfp("-2").multiply(field.newDfp("3")),
+ field.newDfp("-6"),
+ 0, "Multiply #7");
+
+ test(field.newDfp("-2").multiply(3),
+ field.newDfp("-6"),
+ 0, "Multiply #8");
+
+ test(field.newDfp("2").multiply(field.newDfp("-3")),
+ field.newDfp("-6"),
+ 0, "Multiply #9");
+
+ test(field.newDfp("-2").multiply(field.newDfp("-3")),
+ field.newDfp("6"),
+ 0, "Multiply #10");
+
+ //multiply by zero
+
+ test(field.newDfp("-2").multiply(field.newDfp("0")),
+ field.newDfp("-0"),
+ 0, "Multiply #11");
+
+ test(field.newDfp("-2").multiply(0),
+ field.newDfp("-0"),
+ 0, "Multiply #12");
+
+ test(field.newDfp("2").multiply(field.newDfp("0")),
+ field.newDfp("0"),
+ 0, "Multiply #13");
+
+ test(field.newDfp("2").multiply(0),
+ field.newDfp("0"),
+ 0, "Multiply #14");
+
+ test(field.newDfp("2").multiply(pinf),
+ pinf,
+ 0, "Multiply #15");
+
+ test(field.newDfp("2").multiply(ninf),
+ ninf,
+ 0, "Multiply #16");
+
+ test(field.newDfp("-2").multiply(pinf),
+ ninf,
+ 0, "Multiply #17");
+
+ test(field.newDfp("-2").multiply(ninf),
+ pinf,
+ 0, "Multiply #18");
+
+ test(ninf.multiply(field.newDfp("-2")),
+ pinf,
+ 0, "Multiply #18.1");
+
+ test(field.newDfp("5e131071").multiply(2),
+ pinf,
+ DfpField.FLAG_OVERFLOW, "Multiply #19");
+
+ test(field.newDfp("5e131071").multiply(field.newDfp("1.999999999999999")),
+ field.newDfp("9.9999999999999950000e131071"),
+ 0, "Multiply #20");
+
+ test(field.newDfp("-5e131071").multiply(2),
+ ninf,
+ DfpField.FLAG_OVERFLOW, "Multiply #22");
+
+ test(field.newDfp("-5e131071").multiply(field.newDfp("1.999999999999999")),
+ field.newDfp("-9.9999999999999950000e131071"),
+ 0, "Multiply #23");
+
+ test(field.newDfp("1e-65539").multiply(field.newDfp("1e-65539")),
+ field.newDfp("1e-131078"),
+ DfpField.FLAG_UNDERFLOW, "Multiply #24");
+
+ test(field.newDfp("1").multiply(nan),
+ nan,
+ 0, "Multiply #25");
+
+ test(nan.multiply(field.newDfp("1")),
+ nan,
+ 0, "Multiply #26");
+
+ test(nan.multiply(pinf),
+ nan,
+ 0, "Multiply #27");
+
+ test(pinf.multiply(nan),
+ nan,
+ 0, "Multiply #27");
+
+ test(pinf.multiply(field.newDfp("0")),
+ nan,
+ DfpField.FLAG_INVALID, "Multiply #28");
+
+ test(field.newDfp("0").multiply(pinf),
+ nan,
+ DfpField.FLAG_INVALID, "Multiply #29");
+
+ test(pinf.multiply(pinf),
+ pinf,
+ 0, "Multiply #30");
+
+ test(ninf.multiply(pinf),
+ ninf,
+ 0, "Multiply #31");
+
+ test(pinf.multiply(ninf),
+ ninf,
+ 0, "Multiply #32");
+
+ test(ninf.multiply(ninf),
+ pinf,
+ 0, "Multiply #33");
+
+ test(pinf.multiply(1),
+ pinf,
+ 0, "Multiply #34");
+
+ test(pinf.multiply(0),
+ nan,
+ DfpField.FLAG_INVALID, "Multiply #35");
+
+ test(nan.multiply(1),
+ nan,
+ 0, "Multiply #36");
+
+ test(field.newDfp("1").multiply(10000), // out of range
+ nan,
+ DfpField.FLAG_INVALID, "Multiply #37");
+
+ test(field.newDfp("1").multiply(-1), // out of range
+ nan,
+ DfpField.FLAG_INVALID, "Multiply #38");
+ }
+
+ @Test
+ public void testDivide()
+ {
+ test(field.newDfp("1").divide(nan), // divide by NaN = NaN
+ nan,
+ 0, "Divide #1");
+
+ test(nan.divide(field.newDfp("1")), // NaN / number = NaN
+ nan,
+ 0, "Divide #2");
+
+ test(pinf.divide(field.newDfp("1")),
+ pinf,
+ 0, "Divide #3");
+
+ test(pinf.divide(field.newDfp("-1")),
+ ninf,
+ 0, "Divide #4");
+
+ test(pinf.divide(pinf),
+ nan,
+ DfpField.FLAG_INVALID, "Divide #5");
+
+ test(ninf.divide(pinf),
+ nan,
+ DfpField.FLAG_INVALID, "Divide #6");
+
+ test(pinf.divide(ninf),
+ nan,
+ DfpField.FLAG_INVALID, "Divide #7");
+
+ test(ninf.divide(ninf),
+ nan,
+ DfpField.FLAG_INVALID, "Divide #8");
+
+ test(field.newDfp("0").divide(field.newDfp("0")),
+ nan,
+ DfpField.FLAG_DIV_ZERO, "Divide #9");
+
+ test(field.newDfp("1").divide(field.newDfp("0")),
+ pinf,
+ DfpField.FLAG_DIV_ZERO, "Divide #10");
+
+ test(field.newDfp("1").divide(field.newDfp("-0")),
+ ninf,
+ DfpField.FLAG_DIV_ZERO, "Divide #11");
+
+ test(field.newDfp("-1").divide(field.newDfp("0")),
+ ninf,
+ DfpField.FLAG_DIV_ZERO, "Divide #12");
+
+ test(field.newDfp("-1").divide(field.newDfp("-0")),
+ pinf,
+ DfpField.FLAG_DIV_ZERO, "Divide #13");
+
+ test(field.newDfp("1").divide(field.newDfp("3")),
+ field.newDfp("0.33333333333333333333"),
+ DfpField.FLAG_INEXACT, "Divide #14");
+
+ test(field.newDfp("1").divide(field.newDfp("6")),
+ field.newDfp("0.16666666666666666667"),
+ DfpField.FLAG_INEXACT, "Divide #15");
+
+ test(field.newDfp("10").divide(field.newDfp("6")),
+ field.newDfp("1.6666666666666667"),
+ DfpField.FLAG_INEXACT, "Divide #16");
+
+ test(field.newDfp("100").divide(field.newDfp("6")),
+ field.newDfp("16.6666666666666667"),
+ DfpField.FLAG_INEXACT, "Divide #17");
+
+ test(field.newDfp("1000").divide(field.newDfp("6")),
+ field.newDfp("166.6666666666666667"),
+ DfpField.FLAG_INEXACT, "Divide #18");
+
+ test(field.newDfp("10000").divide(field.newDfp("6")),
+ field.newDfp("1666.6666666666666667"),
+ DfpField.FLAG_INEXACT, "Divide #19");
+
+ test(field.newDfp("1").divide(field.newDfp("1")),
+ field.newDfp("1"),
+ 0, "Divide #20");
+
+ test(field.newDfp("1").divide(field.newDfp("-1")),
+ field.newDfp("-1"),
+ 0, "Divide #21");
+
+ test(field.newDfp("-1").divide(field.newDfp("1")),
+ field.newDfp("-1"),
+ 0, "Divide #22");
+
+ test(field.newDfp("-1").divide(field.newDfp("-1")),
+ field.newDfp("1"),
+ 0, "Divide #23");
+
+ test(field.newDfp("1e-65539").divide(field.newDfp("1e65539")),
+ field.newDfp("1e-131078"),
+ DfpField.FLAG_UNDERFLOW, "Divide #24");
+
+ test(field.newDfp("1e65539").divide(field.newDfp("1e-65539")),
+ pinf,
+ DfpField.FLAG_OVERFLOW, "Divide #24");
+
+ test(field.newDfp("2").divide(field.newDfp("1.5")), // test trial-divisor too high
+ field.newDfp("1.3333333333333333"),
+ DfpField.FLAG_INEXACT, "Divide #25");
+
+ test(field.newDfp("2").divide(pinf),
+ field.newDfp("0"),
+ 0, "Divide #26");
+
+ test(field.newDfp("2").divide(ninf),
+ field.newDfp("-0"),
+ 0, "Divide #27");
+
+ test(field.newDfp("0").divide(field.newDfp("1")),
+ field.newDfp("0"),
+ 0, "Divide #28");
+ }
+
+ @Test
+ public void testDivideInt()
+ {
+ test(nan.divide(1), // NaN / number = NaN
+ nan,
+ 0, "DivideInt #1");
+
+ test(pinf.divide(1),
+ pinf,
+ 0, "DivideInt #2");
+
+ test(field.newDfp("0").divide(0),
+ nan,
+ DfpField.FLAG_DIV_ZERO, "DivideInt #3");
+
+ test(field.newDfp("1").divide(0),
+ pinf,
+ DfpField.FLAG_DIV_ZERO, "DivideInt #4");
+
+ test(field.newDfp("-1").divide(0),
+ ninf,
+ DfpField.FLAG_DIV_ZERO, "DivideInt #5");
+
+ test(field.newDfp("1").divide(3),
+ field.newDfp("0.33333333333333333333"),
+ DfpField.FLAG_INEXACT, "DivideInt #6");
+
+ test(field.newDfp("1").divide(6),
+ field.newDfp("0.16666666666666666667"),
+ DfpField.FLAG_INEXACT, "DivideInt #7");
+
+ test(field.newDfp("10").divide(6),
+ field.newDfp("1.6666666666666667"),
+ DfpField.FLAG_INEXACT, "DivideInt #8");
+
+ test(field.newDfp("100").divide(6),
+ field.newDfp("16.6666666666666667"),
+ DfpField.FLAG_INEXACT, "DivideInt #9");
+
+ test(field.newDfp("1000").divide(6),
+ field.newDfp("166.6666666666666667"),
+ DfpField.FLAG_INEXACT, "DivideInt #10");
+
+ test(field.newDfp("10000").divide(6),
+ field.newDfp("1666.6666666666666667"),
+ DfpField.FLAG_INEXACT, "DivideInt #20");
+
+ test(field.newDfp("1").divide(1),
+ field.newDfp("1"),
+ 0, "DivideInt #21");
+
+ test(field.newDfp("1e-131077").divide(10),
+ field.newDfp("1e-131078"),
+ DfpField.FLAG_UNDERFLOW, "DivideInt #22");
+
+ test(field.newDfp("0").divide(1),
+ field.newDfp("0"),
+ 0, "DivideInt #23");
+
+ test(field.newDfp("1").divide(10000),
+ nan,
+ DfpField.FLAG_INVALID, "DivideInt #24");
+
+ test(field.newDfp("1").divide(-1),
+ nan,
+ DfpField.FLAG_INVALID, "DivideInt #25");
+ }
+
+ @Test
+ public void testNextAfter()
+ {
+ test(field.newDfp("1").nextAfter(pinf),
+ field.newDfp("1.0000000000000001"),
+ 0, "NextAfter #1");
+
+ test(field.newDfp("1.0000000000000001").nextAfter(ninf),
+ field.newDfp("1"),
+ 0, "NextAfter #1.5");
+
+ test(field.newDfp("1").nextAfter(ninf),
+ field.newDfp("0.99999999999999999999"),
+ 0, "NextAfter #2");
+
+ test(field.newDfp("0.99999999999999999999").nextAfter(field.newDfp("2")),
+ field.newDfp("1"),
+ 0, "NextAfter #3");
+
+ test(field.newDfp("-1").nextAfter(ninf),
+ field.newDfp("-1.0000000000000001"),
+ 0, "NextAfter #4");
+
+ test(field.newDfp("-1").nextAfter(pinf),
+ field.newDfp("-0.99999999999999999999"),
+ 0, "NextAfter #5");
+
+ test(field.newDfp("-0.99999999999999999999").nextAfter(field.newDfp("-2")),
+ field.newDfp("-1"),
+ 0, "NextAfter #6");
+
+ test(field.newDfp("2").nextAfter(field.newDfp("2")),
+ field.newDfp("2"),
+ 0, "NextAfter #7");
+
+ test(field.newDfp("0").nextAfter(field.newDfp("0")),
+ field.newDfp("0"),
+ 0, "NextAfter #8");
+
+ test(field.newDfp("-2").nextAfter(field.newDfp("-2")),
+ field.newDfp("-2"),
+ 0, "NextAfter #9");
+
+ test(field.newDfp("0").nextAfter(field.newDfp("1")),
+ field.newDfp("1e-131092"),
+ DfpField.FLAG_UNDERFLOW, "NextAfter #10");
+
+ test(field.newDfp("0").nextAfter(field.newDfp("-1")),
+ field.newDfp("-1e-131092"),
+ DfpField.FLAG_UNDERFLOW, "NextAfter #11");
+
+ test(field.newDfp("-1e-131092").nextAfter(pinf),
+ field.newDfp("-0"),
+ DfpField.FLAG_UNDERFLOW|DfpField.FLAG_INEXACT, "Next After #12");
+
+ test(field.newDfp("1e-131092").nextAfter(ninf),
+ field.newDfp("0"),
+ DfpField.FLAG_UNDERFLOW|DfpField.FLAG_INEXACT, "Next After #13");
+
+ test(field.newDfp("9.9999999999999999999e131078").nextAfter(pinf),
+ pinf,
+ DfpField.FLAG_OVERFLOW|DfpField.FLAG_INEXACT, "Next After #14");
+ }
+
+ @Test
+ public void testToString()
+ {
+ Assert.assertEquals("toString #1", "Infinity", pinf.toString());
+ Assert.assertEquals("toString #2", "-Infinity", ninf.toString());
+ Assert.assertEquals("toString #3", "NaN", nan.toString());
+ Assert.assertEquals("toString #4", "NaN", field.newDfp((byte) 1, Dfp.QNAN).toString());
+ Assert.assertEquals("toString #5", "NaN", field.newDfp((byte) 1, Dfp.SNAN).toString());
+ Assert.assertEquals("toString #6", "1.2300000000000000e100", field.newDfp("1.23e100").toString());
+ Assert.assertEquals("toString #7", "-1.2300000000000000e100", field.newDfp("-1.23e100").toString());
+ Assert.assertEquals("toString #8", "12345678.1234", field.newDfp("12345678.1234").toString());
+ Assert.assertEquals("toString #9", "0.00001234", field.newDfp("0.00001234").toString());
+ }
+
+ @Test
+ public void testRound()
+ {
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_DOWN);
+
+ // Round down
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.9")),
+ field.newDfp("12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #1");
+
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.99999999")),
+ field.newDfp("12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #2");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.99999999")),
+ field.newDfp("-12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #3");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_UP);
+
+ // Round up
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.1")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #4");
+
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.0001")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #5");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.1")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #6");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.0001")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #7");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_UP);
+
+ // Round half up
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.4999")),
+ field.newDfp("12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #8");
+
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.5000")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #9");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.4999")),
+ field.newDfp("-12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #10");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.5000")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #11");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_DOWN);
+
+ // Round half down
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.5001")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #12");
+
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.5000")),
+ field.newDfp("12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #13");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.5001")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #14");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.5000")),
+ field.newDfp("-12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #15");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_ODD);
+
+ // Round half odd
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.5000")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #16");
+
+ test(field.newDfp("12345678901234567891").add(field.newDfp("0.5000")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #17");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.5000")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #18");
+
+ test(field.newDfp("-12345678901234567891").add(field.newDfp("-0.5000")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #19");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_CEIL);
+
+ // Round ceil
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.0001")),
+ field.newDfp("12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #20");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.9999")),
+ field.newDfp("-12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #21");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_FLOOR);
+
+ // Round floor
+ test(field.newDfp("12345678901234567890").add(field.newDfp("0.9999")),
+ field.newDfp("12345678901234567890"),
+ DfpField.FLAG_INEXACT, "Round #22");
+
+ test(field.newDfp("-12345678901234567890").add(field.newDfp("-0.0001")),
+ field.newDfp("-12345678901234567891"),
+ DfpField.FLAG_INEXACT, "Round #23");
+
+ field.setRoundingMode(DfpField.RoundingMode.ROUND_HALF_EVEN); // reset
+ }
+
+ @Test
+ public void testCeil()
+ {
+ test(field.newDfp("1234.0000000000000001").ceil(),
+ field.newDfp("1235"),
+ DfpField.FLAG_INEXACT, "Ceil #1");
+ }
+
+ @Test
+ public void testFloor()
+ {
+ test(field.newDfp("1234.9999999999999999").floor(),
+ field.newDfp("1234"),
+ DfpField.FLAG_INEXACT, "Floor #1");
+ }
+
+ @Test
+ public void testRint()
+ {
+ test(field.newDfp("1234.50000000001").rint(),
+ field.newDfp("1235"),
+ DfpField.FLAG_INEXACT, "Rint #1");
+
+ test(field.newDfp("1234.5000").rint(),
+ field.newDfp("1234"),
+ DfpField.FLAG_INEXACT, "Rint #2");
+
+ test(field.newDfp("1235.5000").rint(),
+ field.newDfp("1236"),
+ DfpField.FLAG_INEXACT, "Rint #3");
+ }
+
+ @Test
+ public void testCopySign()
+ {
+ test(Dfp.copysign(field.newDfp("1234."), field.newDfp("-1")),
+ field.newDfp("-1234"),
+ 0, "CopySign #1");
+
+ test(Dfp.copysign(field.newDfp("-1234."), field.newDfp("-1")),
+ field.newDfp("-1234"),
+ 0, "CopySign #2");
+
+ test(Dfp.copysign(field.newDfp("-1234."), field.newDfp("1")),
+ field.newDfp("1234"),
+ 0, "CopySign #3");
+
+ test(Dfp.copysign(field.newDfp("1234."), field.newDfp("1")),
+ field.newDfp("1234"),
+ 0, "CopySign #4");
+ }
+
+ @Test
+ public void testIntValue()
+ {
+ Assert.assertEquals("intValue #1", 1234, field.newDfp("1234").intValue());
+ Assert.assertEquals("intValue #2", -1234, field.newDfp("-1234").intValue());
+ Assert.assertEquals("intValue #3", 1234, field.newDfp("1234.5").intValue());
+ Assert.assertEquals("intValue #4", 1235, field.newDfp("1234.500001").intValue());
+ Assert.assertEquals("intValue #5", 2147483647, field.newDfp("1e1000").intValue());
+ Assert.assertEquals("intValue #6", -2147483648, field.newDfp("-1e1000").intValue());
+ }
+
+ @Test
+ public void testLog10K()
+ {
+ Assert.assertEquals("log10K #1", 1, field.newDfp("123456").log10K());
+ Assert.assertEquals("log10K #2", 2, field.newDfp("123456789").log10K());
+ Assert.assertEquals("log10K #3", 0, field.newDfp("2").log10K());
+ Assert.assertEquals("log10K #3", 0, field.newDfp("1").log10K());
+ Assert.assertEquals("log10K #4", -1, field.newDfp("0.1").log10K());
+ }
+
+ @Test
+ public void testPower10K()
+ {
+ Dfp d = field.newDfp();
+
+ test(d.power10K(0), field.newDfp("1"), 0, "Power10 #1");
+ test(d.power10K(1), field.newDfp("10000"), 0, "Power10 #2");
+ test(d.power10K(2), field.newDfp("100000000"), 0, "Power10 #3");
+
+ test(d.power10K(-1), field.newDfp("0.0001"), 0, "Power10 #4");
+ test(d.power10K(-2), field.newDfp("0.00000001"), 0, "Power10 #5");
+ test(d.power10K(-3), field.newDfp("0.000000000001"), 0, "Power10 #6");
+ }
+
+ @Test
+ public void testLog10()
+ {
+
+ Assert.assertEquals("log10 #1", 1, field.newDfp("12").log10());
+ Assert.assertEquals("log10 #2", 2, field.newDfp("123").log10());
+ Assert.assertEquals("log10 #3", 3, field.newDfp("1234").log10());
+ Assert.assertEquals("log10 #4", 4, field.newDfp("12345").log10());
+ Assert.assertEquals("log10 #5", 5, field.newDfp("123456").log10());
+ Assert.assertEquals("log10 #6", 6, field.newDfp("1234567").log10());
+ Assert.assertEquals("log10 #6", 7, field.newDfp("12345678").log10());
+ Assert.assertEquals("log10 #7", 8, field.newDfp("123456789").log10());
+ Assert.assertEquals("log10 #8", 9, field.newDfp("1234567890").log10());
+ Assert.assertEquals("log10 #9", 10, field.newDfp("12345678901").log10());
+ Assert.assertEquals("log10 #10", 11, field.newDfp("123456789012").log10());
+ Assert.assertEquals("log10 #11", 12, field.newDfp("1234567890123").log10());
+
+ Assert.assertEquals("log10 #12", 0, field.newDfp("2").log10());
+ Assert.assertEquals("log10 #13", 0, field.newDfp("1").log10());
+ Assert.assertEquals("log10 #14", -1, field.newDfp("0.12").log10());
+ Assert.assertEquals("log10 #15", -2, field.newDfp("0.012").log10());
+ }
+
+ @Test
+ public void testPower10()
+ {
+ Dfp d = field.newDfp();
+
+ test(d.power10(0), field.newDfp("1"), 0, "Power10 #1");
+ test(d.power10(1), field.newDfp("10"), 0, "Power10 #2");
+ test(d.power10(2), field.newDfp("100"), 0, "Power10 #3");
+ test(d.power10(3), field.newDfp("1000"), 0, "Power10 #4");
+ test(d.power10(4), field.newDfp("10000"), 0, "Power10 #5");
+ test(d.power10(5), field.newDfp("100000"), 0, "Power10 #6");
+ test(d.power10(6), field.newDfp("1000000"), 0, "Power10 #7");
+ test(d.power10(7), field.newDfp("10000000"), 0, "Power10 #8");
+ test(d.power10(8), field.newDfp("100000000"), 0, "Power10 #9");
+ test(d.power10(9), field.newDfp("1000000000"), 0, "Power10 #10");
+
+ test(d.power10(-1), field.newDfp(".1"), 0, "Power10 #11");
+ test(d.power10(-2), field.newDfp(".01"), 0, "Power10 #12");
+ test(d.power10(-3), field.newDfp(".001"), 0, "Power10 #13");
+ test(d.power10(-4), field.newDfp(".0001"), 0, "Power10 #14");
+ test(d.power10(-5), field.newDfp(".00001"), 0, "Power10 #15");
+ test(d.power10(-6), field.newDfp(".000001"), 0, "Power10 #16");
+ test(d.power10(-7), field.newDfp(".0000001"), 0, "Power10 #17");
+ test(d.power10(-8), field.newDfp(".00000001"), 0, "Power10 #18");
+ test(d.power10(-9), field.newDfp(".000000001"), 0, "Power10 #19");
+ test(d.power10(-10), field.newDfp(".0000000001"), 0, "Power10 #20");
+ }
+
+ @Test
+ public void testRemainder()
+ {
+ test(field.newDfp("10").remainder(field.newDfp("3")),
+ field.newDfp("1"),
+ DfpField.FLAG_INEXACT, "Remainder #1");
+
+ test(field.newDfp("9").remainder(field.newDfp("3")),
+ field.newDfp("0"),
+ 0, "Remainder #2");
+
+ test(field.newDfp("-9").remainder(field.newDfp("3")),
+ field.newDfp("-0"),
+ 0, "Remainder #3");
+ }
+
+ @Test
+ public void testSqrt()
+ {
+ test(field.newDfp("0").sqrt(),
+ field.newDfp("0"),
+ 0, "Sqrt #1");
+
+ test(field.newDfp("-0").sqrt(),
+ field.newDfp("-0"),
+ 0, "Sqrt #2");
+
+ test(field.newDfp("1").sqrt(),
+ field.newDfp("1"),
+ 0, "Sqrt #3");
+
+ test(field.newDfp("2").sqrt(),
+ field.newDfp("1.4142135623730950"),
+ DfpField.FLAG_INEXACT, "Sqrt #4");
+
+ test(field.newDfp("3").sqrt(),
+ field.newDfp("1.7320508075688773"),
+ DfpField.FLAG_INEXACT, "Sqrt #5");
+
+ test(field.newDfp("5").sqrt(),
+ field.newDfp("2.2360679774997897"),
+ DfpField.FLAG_INEXACT, "Sqrt #6");
+
+ test(field.newDfp("500").sqrt(),
+ field.newDfp("22.3606797749978970"),
+ DfpField.FLAG_INEXACT, "Sqrt #6.2");
+
+ test(field.newDfp("50000").sqrt(),
+ field.newDfp("223.6067977499789696"),
+ DfpField.FLAG_INEXACT, "Sqrt #6.3");
+
+ test(field.newDfp("-1").sqrt(),
+ nan,
+ DfpField.FLAG_INVALID, "Sqrt #7");
+
+ test(pinf.sqrt(),
+ pinf,
+ 0, "Sqrt #8");
+
+ test(field.newDfp((byte) 1, Dfp.QNAN).sqrt(),
+ nan,
+ 0, "Sqrt #9");
+
+ test(field.newDfp((byte) 1, Dfp.SNAN).sqrt(),
+ nan,
+ DfpField.FLAG_INVALID, "Sqrt #9");
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/BetaDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/BetaDistributionTest.java
new file mode 100644
index 0000000..f96aa99
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/BetaDistributionTest.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 junit.framework.TestCase;
+import org.apache.commons.math.MathException;
+
+public class BetaDistributionTest extends TestCase {
+ public void testCumulative() throws MathException {
+ double[] x = new double[]{-0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1};
+ // all test data computed using R 2.5
+ checkCumulative(0.1, 0.1,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.4063850939, 0.4397091902, 0.4628041861,
+ 0.4821200456, 0.5000000000, 0.5178799544, 0.5371958139, 0.5602908098,
+ 0.5936149061, 1.0000000000, 1.0000000000});
+ checkCumulative(0.1, 0.5,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.7048336221, 0.7593042194, 0.7951765304,
+ 0.8234948385, 0.8480017124, 0.8706034370, 0.8926585878, 0.9156406404,
+ 0.9423662883, 1.0000000000, 1.0000000000});
+ checkCumulative(0.1, 1.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.7943282347, 0.8513399225, 0.8865681506,
+ 0.9124435366, 0.9330329915, 0.9502002165, 0.9649610951, 0.9779327685,
+ 0.9895192582, 1.0000000000, 1.0000000000});
+ checkCumulative(0.1, 2.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.8658177758, 0.9194471163, 0.9486279211,
+ 0.9671901487, 0.9796846411, 0.9882082252, 0.9939099280, 0.9974914239,
+ 0.9994144508, 1.0000000000, 1.0000000000});
+ checkCumulative(0.1, 4.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.9234991121, 0.9661958941, 0.9842285085,
+ 0.9928444112, 0.9970040660, 0.9989112804, 0.9996895625, 0.9999440793,
+ 0.9999967829, 1.0000000000, 1.0000000000});
+ checkCumulative(0.5, 0.1,
+ x, new double[]{
+ 0.00000000000, 0.00000000000, 0.05763371168, 0.08435935962,
+ 0.10734141216, 0.12939656302, 0.15199828760, 0.17650516146,
+ 0.20482346963, 0.24069578055, 0.29516637795, 1.00000000000, 1.00000000000});
+
+ checkCumulative(0.5, 0.5,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.2048327647, 0.2951672353, 0.3690101196,
+ 0.4359057832, 0.5000000000, 0.5640942168, 0.6309898804, 0.7048327647,
+ 0.7951672353, 1.0000000000, 1.0000000000});
+ checkCumulative(0.5, 1.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.3162277660, 0.4472135955, 0.5477225575,
+ 0.6324555320, 0.7071067812, 0.7745966692, 0.8366600265, 0.8944271910,
+ 0.9486832981, 1.0000000000, 1.0000000000});
+ checkCumulative(0.5, 2.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.4585302607, 0.6260990337, 0.7394254526,
+ 0.8221921916, 0.8838834765, 0.9295160031, 0.9621590305, 0.9838699101,
+ 0.9961174630, 1.0000000000, 1.0000000000});
+ checkCumulative(0.5, 4.0,
+ x, new double[]{
+ 0.0000000000, 0.0000000000, 0.6266250826, 0.8049844719, 0.8987784842,
+ 0.9502644369, 0.9777960959, 0.9914837366, 0.9974556254, 0.9995223859,
+ 0.9999714889, 1.0000000000, 1.0000000000});
+ checkCumulative(1.0, 0.1,
+ x, new double[]{
+ 0.00000000000, 0.00000000000, 0.01048074179, 0.02206723146,
+ 0.03503890488, 0.04979978349, 0.06696700846, 0.08755646344,
+ 0.11343184943, 0.14866007748, 0.20567176528, 1.00000000000, 1.00000000000});
+ checkCumulative(1.0, 0.5,
+ x, new double[]{
+ 0.00000000000, 0.00000000000, 0.05131670195, 0.10557280900,
+ 0.16333997347, 0.22540333076, 0.29289321881, 0.36754446797,
+ 0.45227744249, 0.55278640450, 0.68377223398, 1.00000000000, 1.00000000000});
+ checkCumulative(1, 1,
+ x, new double[]{
+ 0.0, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.0});
+ checkCumulative(1, 2,
+ x, new double[]{
+ 0.00, 0.00, 0.19, 0.36, 0.51, 0.64, 0.75, 0.84, 0.91, 0.96, 0.99, 1.00, 1.00});
+ checkCumulative(1, 4,
+ x, new double[]{
+ 0.0000, 0.0000, 0.3439, 0.5904, 0.7599, 0.8704, 0.9375, 0.9744, 0.9919,
+ 0.9984, 0.9999, 1.0000, 1.0000});
+ checkCumulative(2.0, 0.1,
+ x, new double[]{
+ 0.0000000000000, 0.0000000000000, 0.0005855492117, 0.0025085760862,
+ 0.0060900720266, 0.0117917748341, 0.0203153588864, 0.0328098512512,
+ 0.0513720788952, 0.0805528836776, 0.1341822241505, 1.0000000000000, 1.0000000000000});
+ checkCumulative(2, 1,
+ x, new double[]{
+ 0.00, 0.00, 0.01, 0.04, 0.09, 0.16, 0.25, 0.36, 0.49, 0.64, 0.81, 1.00, 1.00});
+ checkCumulative(2.0, 0.5,
+ x, new double[]{
+ 0.000000000000, 0.000000000000, 0.003882537047, 0.016130089900,
+ 0.037840969486, 0.070483996910, 0.116116523517, 0.177807808356,
+ 0.260574547368, 0.373900966300, 0.541469739276, 1.000000000000, 1.000000000000});
+ checkCumulative(2, 2,
+ x, new double[]{
+ 0.000, 0.000, 0.028, 0.104, 0.216, 0.352, 0.500, 0.648, 0.784, 0.896, 0.972, 1.000, 1.000});
+ checkCumulative(2, 4,
+ x, new double[]{
+ 0.00000, 0.00000, 0.08146, 0.26272, 0.47178, 0.66304, 0.81250, 0.91296,
+ 0.96922, 0.99328, 0.99954, 1.00000, 1.00000});
+ checkCumulative(4.0, 0.1,
+ x, new double[]{
+ 0.000000000e+00, 0.000000000e+00, 3.217128269e-06, 5.592070271e-05,
+ 3.104375474e-04, 1.088719595e-03, 2.995933981e-03, 7.155588777e-03,
+ 1.577149153e-02, 3.380410585e-02, 7.650088789e-02, 1.000000000e+00, 1.000000000e+00});
+ checkCumulative(4.0, 0.5,
+ x, new double[]{
+ 0.000000000e+00, 0.000000000e+00, 2.851114863e-05, 4.776140576e-04,
+ 2.544374616e-03, 8.516263371e-03, 2.220390414e-02, 4.973556312e-02,
+ 1.012215158e-01, 1.950155281e-01, 3.733749174e-01, 1.000000000e+00, 1.000000000e+00});
+ checkCumulative(4, 1,
+ x, new double[]{
+ 0.0000, 0.0000, 0.0001, 0.0016, 0.0081, 0.0256, 0.0625, 0.1296, 0.2401,
+ 0.4096, 0.6561, 1.0000, 1.0000});
+ checkCumulative(4, 2,
+ x, new double[]{
+ 0.00000, 0.00000, 0.00046, 0.00672, 0.03078, 0.08704, 0.18750, 0.33696,
+ 0.52822, 0.73728, 0.91854, 1.00000, 1.00000});
+ checkCumulative(4, 4,
+ x, new double[]{
+ 0.000000, 0.000000, 0.002728, 0.033344, 0.126036, 0.289792, 0.500000,
+ 0.710208, 0.873964, 0.966656, 0.997272, 1.000000, 1.000000});
+
+ }
+
+ private void checkCumulative(double alpha, double beta, double[] x, double[] cumes) throws MathException {
+ BetaDistribution d = new BetaDistributionImpl(alpha, beta);
+ for (int i = 0; i < x.length; i++) {
+ assertEquals(cumes[i], d.cumulativeProbability(x[i]), 1e-8);
+ }
+
+ for (int i = 1; i < x.length - 1; i++) {
+ assertEquals(x[i], d.inverseCumulativeProbability(cumes[i]), 1e-5);
+ }
+ }
+
+ public void testDensity() throws MathException {
+ double[] x = new double[]{1e-6, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
+ checkDensity(0.1, 0.1,
+ x, new double[]{
+ 12741.2357380649, 0.4429889586665234, 2.639378715e-01, 2.066393611e-01,
+ 1.832401831e-01, 1.766302780e-01, 1.832404579e-01, 2.066400696e-01,
+ 2.639396531e-01, 4.429925026e-01});
+ checkDensity(0.1, 0.5,
+ x, new double[]{
+ 2.218377102e+04, 7.394524202e-01, 4.203020268e-01, 3.119435533e-01,
+ 2.600787829e-01, 2.330648626e-01, 2.211408259e-01, 2.222728708e-01,
+ 2.414013907e-01, 3.070567405e-01});
+ checkDensity(0.1, 1.0,
+ x, new double[]{
+ 2.511886432e+04, 7.943210858e-01, 4.256680458e-01, 2.955218303e-01,
+ 2.281103709e-01, 1.866062624e-01, 1.583664652e-01, 1.378514078e-01,
+ 1.222414585e-01, 1.099464743e-01});
+ checkDensity(0.1, 2.0,
+ x, new double[]{
+ 2.763072312e+04, 7.863770012e-01, 3.745874120e-01, 2.275514842e-01,
+ 1.505525939e-01, 1.026332391e-01, 6.968107049e-02, 4.549081293e-02,
+ 2.689298641e-02, 1.209399123e-02});
+ checkDensity(0.1, 4.0,
+ x, new double[]{
+ 2.997927462e+04, 6.911058917e-01, 2.601128486e-01, 1.209774010e-01,
+ 5.880564714e-02, 2.783915474e-02, 1.209657335e-02, 4.442148268e-03,
+ 1.167143939e-03, 1.312171805e-04});
+ checkDensity(0.5, 0.1,
+ x, new double[]{
+ 88.3152184726, 0.3070542841, 0.2414007269, 0.2222727015,
+ 0.2211409364, 0.2330652355, 0.2600795198, 0.3119449793,
+ 0.4203052841, 0.7394649088});
+ checkDensity(0.5, 0.5,
+ x, new double[]{
+ 318.3100453389, 1.0610282383, 0.7957732234, 0.6946084565,
+ 0.6497470636, 0.6366197724, 0.6497476051, 0.6946097796,
+ 0.7957762075, 1.0610376697});
+ checkDensity(0.5, 1.0,
+ x, new double[]{
+ 500.0000000000, 1.5811309244, 1.1180311937, 0.9128694077,
+ 0.7905684268, 0.7071060741, 0.6454966865, 0.5976138778,
+ 0.5590166450, 0.5270459839});
+ checkDensity(0.5, 2.0,
+ x, new double[]{
+ 749.99925000000, 2.134537420613655, 1.34163575536, 0.95851150881,
+ 0.71151039830, 0.53032849490, 0.38729704363, 0.26892534859,
+ 0.16770415497, 0.07905610701});
+ checkDensity(0.5, 4.0,
+ x, new double[]{
+ 1.093746719e+03, 2.52142232809988, 1.252190241e+00, 6.849343920e-01,
+ 3.735417140e-01, 1.933481570e-01, 9.036885833e-02, 3.529621669e-02,
+ 9.782644546e-03, 1.152878503e-03});
+ checkDensity(1.0, 0.1,
+ x, new double[]{
+ 0.1000000900, 0.1099466942, 0.1222417336, 0.1378517623, 0.1583669403,
+ 0.1866069342, 0.2281113974, 0.2955236034, 0.4256718768,
+ 0.7943353837});
+ checkDensity(1.0, 0.5,
+ x, new double[]{
+ 0.5000002500, 0.5270465695, 0.5590173438, 0.5976147315, 0.6454977623,
+ 0.7071074883, 0.7905704033, 0.9128724506,
+ 1.1180367838, 1.5811467358});
+ checkDensity(1, 1,
+ x, new double[]{
+ 1, 1, 1,
+ 1, 1, 1, 1,
+ 1, 1, 1});
+ checkDensity(1, 2,
+ x, new double[]{
+ 1.999998, 1.799998, 1.599998, 1.399998, 1.199998, 0.999998, 0.799998,
+ 0.599998, 0.399998,
+ 0.199998});
+ checkDensity(1, 4,
+ x, new double[]{
+ 3.999988000012, 2.915990280011, 2.047992320010, 1.371994120008,
+ 0.863995680007, 0.499997000006, 0.255998080005, 0.107998920004,
+ 0.031999520002, 0.003999880001});
+ checkDensity(2.0, 0.1,
+ x, new double[]{
+ 1.100000990e-07, 1.209425730e-02, 2.689331586e-02, 4.549123318e-02,
+ 6.968162794e-02, 1.026340191e-01, 1.505537732e-01, 2.275534997e-01,
+ 3.745917198e-01, 7.863929037e-01});
+ checkDensity(2.0, 0.5,
+ x, new double[]{
+ 7.500003750e-07, 7.905777599e-02, 1.677060417e-01, 2.689275256e-01,
+ 3.872996256e-01, 5.303316769e-01, 7.115145488e-01, 9.585174425e-01,
+ 1.341645818e+00, 2.134537420613655});
+ checkDensity(2, 1,
+ x, new double[]{
+ 0.000002, 0.200002, 0.400002, 0.600002, 0.800002, 1.000002, 1.200002,
+ 1.400002, 1.600002,
+ 1.800002});
+ checkDensity(2, 2,
+ x, new double[]{
+ 5.9999940e-06, 5.4000480e-01, 9.6000360e-01, 1.2600024e+00,
+ 1.4400012e+00, 1.5000000e+00, 1.4399988e+00, 1.2599976e+00,
+ 9.5999640e-01, 5.3999520e-01});
+ checkDensity(2, 4,
+ x, new double[]{
+ 0.00001999994, 1.45800971996, 2.04800255997, 2.05799803998,
+ 1.72799567999, 1.24999500000, 0.76799552000, 0.37799676001,
+ 0.12799824001, 0.01799948000});
+ checkDensity(4.0, 0.1,
+ x, new double[]{
+ 1.193501074e-19, 1.312253162e-04, 1.167181580e-03, 4.442248535e-03,
+ 1.209679109e-02, 2.783958903e-02, 5.880649983e-02, 1.209791638e-01,
+ 2.601171405e-01, 6.911229392e-01});
+ checkDensity(4.0, 0.5,
+ x, new double[]{
+ 1.093750547e-18, 1.152948959e-03, 9.782950259e-03, 3.529697305e-02,
+ 9.037036449e-02, 1.933508639e-01, 3.735463833e-01, 6.849425461e-01,
+ 1.252205894e+00, 2.52142232809988});
+ checkDensity(4, 1,
+ x, new double[]{
+ 4.000000000e-18, 4.000120001e-03, 3.200048000e-02, 1.080010800e-01,
+ 2.560019200e-01, 5.000030000e-01, 8.640043200e-01, 1.372005880e+00,
+ 2.048007680e+00, 2.916009720e+00});
+ checkDensity(4, 2,
+ x, new double[]{
+ 1.999998000e-17, 1.800052000e-02, 1.280017600e-01, 3.780032400e-01,
+ 7.680044800e-01, 1.250005000e+00, 1.728004320e+00, 2.058001960e+00,
+ 2.047997440e+00, 1.457990280e+00});
+ checkDensity(4, 4,
+ x, new double[]{
+ 1.399995800e-16, 1.020627216e-01, 5.734464512e-01, 1.296547409e+00,
+ 1.935364838e+00, 2.187500000e+00, 1.935355162e+00, 1.296532591e+00,
+ 5.734335488e-01, 1.020572784e-01});
+
+ }
+
+ private void checkDensity(double alpha, double beta, double[] x, double[] expected) throws MathException {
+ BetaDistribution d = new BetaDistributionImpl(alpha, beta);
+ for (int i = 0; i < x.length; i++) {
+ assertEquals(String.format("density at x=%.1f for alpha=%.1f, beta=%.1f", x[i], alpha, beta), expected[i], d.density(x[i]), 1e-5);
+ }
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ BetaDistributionImpl dist;
+
+ dist = new BetaDistributionImpl(1, 1);
+ assertEquals(dist.getNumericalMean(), 0.5, tol);
+ assertEquals(dist.getNumericalVariance(), 1.0 / 12.0, tol);
+
+ dist.setAlpha(2);
+ dist.setBeta(5);
+ assertEquals(dist.getNumericalMean(), 2.0 / 7.0, tol);
+ assertEquals(dist.getNumericalVariance(), 10.0 / (49.0 * 8.0), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/BinomialDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/BinomialDistributionTest.java
new file mode 100644
index 0000000..b4ae2b3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/BinomialDistributionTest.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.distribution;
+
+/**
+ * Test cases for BinomialDistribution. Extends IntegerDistributionAbstractTest.
+ * See class javadoc for IntegerDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2009-09-05 12:36:48 -0500 (Sat, 05 Sep
+ * 2009) $
+ */
+public class BinomialDistributionTest extends IntegerDistributionAbstractTest {
+
+ /**
+ * Constructor for BinomialDistributionTest.
+ *
+ * @param name
+ */
+ public BinomialDistributionTest(String name) {
+ super(name);
+ }
+
+ // -------------- Implementations for abstract methods
+ // -----------------------
+
+ /** Creates the default discrete distribution instance to use in tests. */
+ @Override
+ public IntegerDistribution makeDistribution() {
+ return new BinomialDistributionImpl(10, 0.70);
+ }
+
+ /** Creates the default probability density test input values */
+ @Override
+ public int[] makeDensityTestPoints() {
+ return new int[] { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] { 0d, 0.0000059049d, 0.000137781d, 0.0014467d,
+ 0.00900169d, 0.0367569d, 0.102919d, 0.200121d, 0.266828d,
+ 0.233474d, 0.121061d, 0.0282475d, 0d };
+ }
+
+ /** Creates the default cumulative probability density test input values */
+ @Override
+ public int[] makeCumulativeTestPoints() {
+ return makeDensityTestPoints();
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] { 0d, 0.0000d, 0.0001d, 0.0016d, 0.0106d, 0.0473d,
+ 0.1503d, 0.3504d, 0.6172d, 0.8507d, 0.9718d, 1d, 1d };
+ }
+
+ /** Creates the default inverse cumulative probability test input values */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] { 0, 0.001d, 0.010d, 0.025d, 0.050d, 0.100d,
+ 0.999d, 0.990d, 0.975d, 0.950d, 0.900d, 1 };
+ }
+
+ /**
+ * Creates the default inverse cumulative probability density test expected
+ * values
+ */
+ @Override
+ public int[] makeInverseCumulativeTestValues() {
+ return new int[] { -1, 1, 2, 3, 4, 4, 9, 9, 9, 8, 8, Integer.MAX_VALUE };
+ }
+
+ // ----------------- Additional test cases ---------------------------------
+
+ /** Test degenerate case p = 0 */
+ public void testDegenerate0() throws Exception {
+ setDistribution(new BinomialDistributionImpl(5, 0.0d));
+ setCumulativeTestPoints(new int[] { -1, 0, 1, 5, 10 });
+ setCumulativeTestValues(new double[] { 0d, 1d, 1d, 1d, 1d });
+ setDensityTestPoints(new int[] { -1, 0, 1, 10, 11 });
+ setDensityTestValues(new double[] { 0d, 1d, 0d, 0d, 0d });
+ setInverseCumulativeTestPoints(new double[] { 0.1d, 0.5d });
+ setInverseCumulativeTestValues(new int[] { -1, -1 });
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /** Test degenerate case p = 1 */
+ public void testDegenerate1() throws Exception {
+ setDistribution(new BinomialDistributionImpl(5, 1.0d));
+ setCumulativeTestPoints(new int[] { -1, 0, 1, 2, 5, 10 });
+ setCumulativeTestValues(new double[] { 0d, 0d, 0d, 0d, 1d, 1d });
+ setDensityTestPoints(new int[] { -1, 0, 1, 2, 5, 10 });
+ setDensityTestValues(new double[] { 0d, 0d, 0d, 0d, 1d, 0d });
+ setInverseCumulativeTestPoints(new double[] { 0.1d, 0.5d });
+ setInverseCumulativeTestValues(new int[] { 4, 4 });
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ BinomialDistributionImpl dist;
+
+ dist = new BinomialDistributionImpl(10, 0.5);
+ assertEquals(dist.getNumericalMean(), 10d * 0.5d, tol);
+ assertEquals(dist.getNumericalVariance(), 10d * 0.5d * 0.5d, tol);
+
+ dist.setNumberOfTrials(30);
+ dist.setProbabilityOfSuccess(0.3);
+ assertEquals(dist.getNumericalMean(), 30d * 0.3d, tol);
+ assertEquals(dist.getNumericalVariance(), 30d * 0.3d * (1d - 0.3d), tol);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/CauchyDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/CauchyDistributionTest.java
new file mode 100644
index 0000000..12ce219
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/CauchyDistributionTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.distribution;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for CauchyDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class CauchyDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for CauchyDistributionTest.
+ * @param arg0
+ */
+ public CauchyDistributionTest(String arg0) {
+ super(arg0);
+ }
+
+ // --------------------- Override tolerance --------------
+ protected double defaultTolerance = NormalDistributionImpl.DEFAULT_INVERSE_ABSOLUTE_ACCURACY;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(defaultTolerance);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public CauchyDistribution makeDistribution() {
+ return new CauchyDistributionImpl(1.2, 2.1);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R 2.9.2
+ return new double[] {-667.24856187, -65.6230835029, -25.4830299460, -12.0588781808,
+ -5.26313542807, 669.64856187, 68.0230835029, 27.8830299460, 14.4588781808, 7.66313542807};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {1.49599158008e-06, 0.000149550440335, 0.000933076881878, 0.00370933207799, 0.0144742330437,
+ 1.49599158008e-06, 0.000149550440335, 0.000933076881878, 0.00370933207799, 0.0144742330437};
+ }
+
+ //---------------------------- Additional test cases -------------------------
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0.0, 1.0});
+ setInverseCumulativeTestValues(
+ new double[] {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testMedian() {
+ CauchyDistribution distribution = (CauchyDistribution) getDistribution();
+ double expected = FastMath.random();
+ distribution.setMedian(expected);
+ assertEquals(expected, distribution.getMedian(), 0.0);
+ }
+
+ public void testScale() {
+ CauchyDistribution distribution = (CauchyDistribution) getDistribution();
+ double expected = FastMath.random();
+ distribution.setScale(expected);
+ assertEquals(expected, distribution.getScale(), 0.0);
+ }
+
+ public void testSetScale() {
+ CauchyDistribution distribution = (CauchyDistribution) getDistribution();
+ try {
+ distribution.setScale(0.0);
+ fail("Can not have 0.0 scale.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ distribution.setScale(-1.0);
+ fail("Can not have negative scale.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testMomonts() {
+ CauchyDistributionImpl dist;
+
+ dist = new CauchyDistributionImpl(10.2, 0.15);
+ assertTrue(Double.isNaN(dist.getNumericalMean()));
+ assertTrue(Double.isNaN(dist.getNumericalVariance()));
+
+ dist.setMedian(23.12);
+ dist.setScale(2.12);
+ assertTrue(Double.isNaN(dist.getNumericalMean()));
+ assertTrue(Double.isNaN(dist.getNumericalVariance()));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/ChiSquareDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/ChiSquareDistributionTest.java
new file mode 100644
index 0000000..40636ba
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/ChiSquareDistributionTest.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.distribution;
+
+/**
+ * Test cases for ChiSquareDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class ChiSquareDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for ChiSquareDistributionTest.
+ * @param name
+ */
+ public ChiSquareDistributionTest(String name) {
+ super(name);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public ChiSquaredDistribution makeDistribution() {
+ return new ChiSquaredDistributionImpl(5.0);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {0.210212602629, 0.554298076728, 0.831211613487, 1.14547622606, 1.61030798696,
+ 20.5150056524, 15.0862724694, 12.8325019940, 11.0704976935, 9.23635689978};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default inverse cumulative probability test input values */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] {0, 0.001d, 0.01d, 0.025d, 0.05d, 0.1d, 0.999d,
+ 0.990d, 0.975d, 0.950d, 0.900d, 1};
+ }
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ @Override
+ public double[] makeInverseCumulativeTestValues() {
+ return new double[] {0, 0.210212602629, 0.554298076728, 0.831211613487, 1.14547622606, 1.61030798696,
+ 20.5150056524, 15.0862724694, 12.8325019940, 11.0704976935, 9.23635689978,
+ Double.POSITIVE_INFINITY};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.0115379817652, 0.0415948507811, 0.0665060119842, 0.0919455953114, 0.121472591024,
+ 0.000433630076361, 0.00412780610309, 0.00999340341045, 0.0193246438937, 0.0368460089216};
+ }
+
+ // --------------------- Override tolerance --------------
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(1e-9);
+ }
+
+ //---------------------------- Additional test cases -------------------------
+
+ public void testSmallDf() throws Exception {
+ setDistribution(new ChiSquaredDistributionImpl(0.1d));
+ setTolerance(1E-4);
+ // quantiles computed using R version 1.8.1 (linux version)
+ setCumulativeTestPoints(new double[] {1.168926E-60, 1.168926E-40, 1.063132E-32,
+ 1.144775E-26, 1.168926E-20, 5.472917, 2.175255, 1.13438,
+ 0.5318646, 0.1526342});
+ setInverseCumulativeTestValues(getCumulativeTestPoints());
+ setInverseCumulativeTestPoints(getCumulativeTestValues());
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testDfAccessors() {
+ ChiSquaredDistribution distribution = (ChiSquaredDistribution) getDistribution();
+ assertEquals(5d, distribution.getDegreesOfFreedom(), Double.MIN_VALUE);
+ distribution.setDegreesOfFreedom(4d);
+ assertEquals(4d, distribution.getDegreesOfFreedom(), Double.MIN_VALUE);
+ try {
+ distribution.setDegreesOfFreedom(0d);
+ fail("Expecting IllegalArgumentException for df = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testDensity() {
+ double[] x = new double[]{-0.1, 1e-6, 0.5, 1, 2, 5};
+ //R 2.5: print(dchisq(x, df=1), digits=10)
+ checkDensity(1, x, new double[]{0.00000000000, 398.94208093034, 0.43939128947, 0.24197072452, 0.10377687436, 0.01464498256});
+ //R 2.5: print(dchisq(x, df=0.1), digits=10)
+ checkDensity(0.1, x, new double[]{0.000000000e+00, 2.486453997e+04, 7.464238732e-02, 3.009077718e-02, 9.447299159e-03, 8.827199396e-04});
+ //R 2.5: print(dchisq(x, df=2), digits=10)
+ checkDensity(2, x, new double[]{0.00000000000, 0.49999975000, 0.38940039154, 0.30326532986, 0.18393972059, 0.04104249931});
+ //R 2.5: print(dchisq(x, df=10), digits=10)
+ checkDensity(10, x, new double[]{0.000000000e+00, 1.302082682e-27, 6.337896998e-05, 7.897534632e-04, 7.664155024e-03, 6.680094289e-02});
+ }
+
+ private void checkDensity(double df, double[] x, double[] expected) {
+ ChiSquaredDistribution d = new ChiSquaredDistributionImpl(df);
+ for (int i = 0; i < x.length; i++) {
+ assertEquals(expected[i], d.density(x[i]), 1e-5);
+ }
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ ChiSquaredDistributionImpl dist;
+
+ dist = new ChiSquaredDistributionImpl(1500);
+ assertEquals(dist.getNumericalMean(), 1500, tol);
+ assertEquals(dist.getNumericalVariance(), 3000, tol);
+
+ dist.setDegreesOfFreedom(1.12);
+ assertEquals(dist.getNumericalMean(), 1.12, tol);
+ assertEquals(dist.getNumericalVariance(), 2.24, tol);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java b/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java
new file mode 100644
index 0000000..cc06acb
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.distribution;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Abstract base class for {@link ContinuousDistribution} tests.
+ * <p>
+ * To create a concrete test class for a continuous distribution
+ * implementation, first implement makeDistribution() to return a distribution
+ * instance to use in tests. Then implement each of the test data generation
+ * methods below. In each case, the test points and test values arrays
+ * returned represent parallel arrays of inputs and expected values for the
+ * distribution returned by makeDistribution(). Default implementations
+ * are provided for the makeInverseXxx methods that just invert the mapping
+ * defined by the arrays returned by the makeCumulativeXxx methods.
+ * <p>
+ * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
+ * makeCumulativeTestValues() -- expected cumulative probabilites
+ * makeDensityTestValues() -- expected density values at cumulativeTestPoints
+ * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf
+ * makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * <p>
+ * To implement additional test cases with different distribution instances and
+ * test data, use the setXxx methods for the instance data in test cases and
+ * call the verifyXxx methods to verify results.
+ * <p>
+ * Error tolerance can be overriden by implementing getTolerance().
+ * <p>
+ * Test data should be validated against reference tables or other packages
+ * where possible, and the source of the reference data and/or validation
+ * should be documented in the test cases. A framework for validating
+ * distribution data against R is included in the /src/test/R source tree.
+ * <p>
+ * See {@link NormalDistributionTest} and {@link ChiSquareDistributionTest}
+ * for examples.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public abstract class ContinuousDistributionAbstractTest extends TestCase {
+
+//-------------------- Private test instance data -------------------------
+ /** Distribution instance used to perform tests */
+ private ContinuousDistribution distribution;
+
+ /** Tolerance used in comparing expected and returned values */
+ private double tolerance = 1E-4;
+
+ /** Arguments used to test cumulative probability density calculations */
+ private double[] cumulativeTestPoints;
+
+ /** Values used to test cumulative probability density calculations */
+ private double[] cumulativeTestValues;
+
+ /** Arguments used to test inverse cumulative probability density calculations */
+ private double[] inverseCumulativeTestPoints;
+
+ /** Values used to test inverse cumulative probability density calculations */
+ private double[] inverseCumulativeTestValues;
+
+ /** Values used to test density calculations */
+ private double[] densityTestValues;
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * Constructor for ContinuousDistributionAbstractTest.
+ * @param name
+ */
+ public ContinuousDistributionAbstractTest(String name) {
+ super(name);
+ }
+
+ //-------------------- Abstract methods -----------------------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ public abstract ContinuousDistribution makeDistribution();
+
+ /** Creates the default cumulative probability test input values */
+ public abstract double[] makeCumulativeTestPoints();
+
+ /** Creates the default cumulative probability test expected values */
+ public abstract double[] makeCumulativeTestValues();
+
+ /** Creates the default density test expected values */
+ public abstract double[] makeDensityTestValues();
+
+ //---- Default implementations of inverse test data generation methods ----
+
+ /** Creates the default inverse cumulative probability test input values */
+ public double[] makeInverseCumulativeTestPoints() {
+ return makeCumulativeTestValues();
+ }
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ public double[] makeInverseCumulativeTestValues() {
+ return makeCumulativeTestPoints();
+ }
+
+ //-------------------- Setup / tear down ----------------------------------
+
+ /**
+ * Setup sets all test instance data to default values
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ distribution = makeDistribution();
+ cumulativeTestPoints = makeCumulativeTestPoints();
+ cumulativeTestValues = makeCumulativeTestValues();
+ inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
+ inverseCumulativeTestValues = makeInverseCumulativeTestValues();
+ densityTestValues = makeDensityTestValues();
+ }
+
+ /**
+ * Cleans up test instance data
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ distribution = null;
+ cumulativeTestPoints = null;
+ cumulativeTestValues = null;
+ inverseCumulativeTestPoints = null;
+ inverseCumulativeTestValues = null;
+ densityTestValues = null;
+ }
+
+ //-------------------- Verification methods -------------------------------
+
+ /**
+ * Verifies that cumulative probability density calculations match expected values
+ * using current test instance data
+ */
+ protected void verifyCumulativeProbabilities() throws Exception {
+ for (int i = 0; i < cumulativeTestPoints.length; i++) {
+ TestUtils.assertEquals("Incorrect cumulative probability value returned for "
+ + cumulativeTestPoints[i], cumulativeTestValues[i],
+ distribution.cumulativeProbability(cumulativeTestPoints[i]),
+ getTolerance());
+ }
+ }
+
+ /**
+ * Verifies that inverse cumulative probability density calculations match expected values
+ * using current test instance data
+ */
+ protected void verifyInverseCumulativeProbabilities() throws Exception {
+ for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
+ TestUtils.assertEquals("Incorrect inverse cumulative probability value returned for "
+ + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i],
+ distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]),
+ getTolerance());
+ }
+ }
+
+ /**
+ * Verifies that density calculations match expected values
+ */
+ protected void verifyDensities() throws Exception {
+ for (int i = 0; i < cumulativeTestPoints.length; i++) {
+ TestUtils.assertEquals("Incorrect probability density value returned for "
+ + cumulativeTestPoints[i], densityTestValues[i],
+ //TODO: remove cast when density(double) is added to ContinuousDistribution
+ ((AbstractContinuousDistribution) distribution).density(cumulativeTestPoints[i]),
+ getTolerance());
+ }
+ }
+
+ //------------------------ Default test cases -----------------------------
+
+ /**
+ * Verifies that cumulative probability density calculations match expected values
+ * using default test instance data
+ */
+ public void testCumulativeProbabilities() throws Exception {
+ verifyCumulativeProbabilities();
+ }
+
+ /**
+ * Verifies that inverse cumulative probability density calculations match expected values
+ * using default test instance data
+ */
+ public void testInverseCumulativeProbabilities() throws Exception {
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /**
+ * Verifies that density calculations return expected values
+ * for default test instance data
+ */
+ public void testDensities() throws Exception {
+ verifyDensities();
+ }
+
+ /**
+ * Verifies that probability computations are consistent
+ */
+ public void testConsistency() throws Exception {
+ for (int i=1; i < cumulativeTestPoints.length; i++) {
+
+ // check that cdf(x, x) = 0
+ TestUtils.assertEquals(0d,
+ distribution.cumulativeProbability
+ (cumulativeTestPoints[i], cumulativeTestPoints[i]), tolerance);
+
+ // check that P(a < X < b) = P(X < b) - P(X < a)
+ double upper = FastMath.max(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
+ double lower = FastMath.min(cumulativeTestPoints[i], cumulativeTestPoints[i -1]);
+ double diff = distribution.cumulativeProbability(upper) -
+ distribution.cumulativeProbability(lower);
+ double direct = distribution.cumulativeProbability(lower, upper);
+ TestUtils.assertEquals("Inconsistent cumulative probabilities for ("
+ + lower + "," + upper + ")", diff, direct, tolerance);
+ }
+ }
+
+ /**
+ * Verifies that illegal arguments are correctly handled
+ */
+ public void testIllegalArguments() throws Exception {
+ try {
+ distribution.cumulativeProbability(1, 0);
+ fail("Expecting IllegalArgumentException for bad cumulativeProbability interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.inverseCumulativeProbability(-1);
+ fail("Expecting IllegalArgumentException for p = -1");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.inverseCumulativeProbability(2);
+ fail("Expecting IllegalArgumentException for p = 2");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Test sampling
+ */
+ public void testSampling() throws Exception {
+ AbstractContinuousDistribution dist = (AbstractContinuousDistribution) makeDistribution();
+ final int sampleSize = 1000;
+ double[] sample = dist.sample(sampleSize);
+ double[] quartiles = TestUtils.getDistributionQuartiles(dist);
+ double[] expected = {250, 250, 250, 250};
+ long[] counts = new long[4];
+ dist.reseedRandomGenerator(1000); // Use fixed seed
+ for (int i = 0; i < sampleSize; i++) {
+ TestUtils.updateCounts(sample[i], counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ //------------------ Getters / Setters for test instance data -----------
+ /**
+ * @return Returns the cumulativeTestPoints.
+ */
+ protected double[] getCumulativeTestPoints() {
+ return cumulativeTestPoints;
+ }
+
+ /**
+ * @param cumulativeTestPoints The cumulativeTestPoints to set.
+ */
+ protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
+ this.cumulativeTestPoints = cumulativeTestPoints;
+ }
+
+ /**
+ * @return Returns the cumulativeTestValues.
+ */
+ protected double[] getCumulativeTestValues() {
+ return cumulativeTestValues;
+ }
+
+ /**
+ * @param cumulativeTestValues The cumulativeTestValues to set.
+ */
+ protected void setCumulativeTestValues(double[] cumulativeTestValues) {
+ this.cumulativeTestValues = cumulativeTestValues;
+ }
+
+ protected double[] getDensityTestValues() {
+ return densityTestValues;
+ }
+
+ protected void setDensityTestValues(double[] densityTestValues) {
+ this.densityTestValues = densityTestValues;
+ }
+
+ /**
+ * @return Returns the distribution.
+ */
+ protected ContinuousDistribution getDistribution() {
+ return distribution;
+ }
+
+ /**
+ * @param distribution The distribution to set.
+ */
+ protected void setDistribution(AbstractContinuousDistribution distribution) {
+ this.distribution = distribution;
+ }
+
+ /**
+ * @return Returns the inverseCumulativeTestPoints.
+ */
+ protected double[] getInverseCumulativeTestPoints() {
+ return inverseCumulativeTestPoints;
+ }
+
+ /**
+ * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
+ */
+ protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
+ this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
+ }
+
+ /**
+ * @return Returns the inverseCumulativeTestValues.
+ */
+ protected double[] getInverseCumulativeTestValues() {
+ return inverseCumulativeTestValues;
+ }
+
+ /**
+ * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
+ */
+ protected void setInverseCumulativeTestValues(double[] inverseCumulativeTestValues) {
+ this.inverseCumulativeTestValues = inverseCumulativeTestValues;
+ }
+
+ /**
+ * @return Returns the tolerance.
+ */
+ protected double getTolerance() {
+ return tolerance;
+ }
+
+ /**
+ * @param tolerance The tolerance to set.
+ */
+ protected void setTolerance(double tolerance) {
+ this.tolerance = tolerance;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/ExponentialDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/ExponentialDistributionTest.java
new file mode 100644
index 0000000..e6dcf6a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/ExponentialDistributionTest.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.distribution;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for ExponentialDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class ExponentialDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for ExponentialDistributionTest.
+ * @param name
+ */
+ public ExponentialDistributionTest(String name) {
+ super(name);
+ }
+
+ // --------------------- Override tolerance --------------
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(1E-9);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public ExponentialDistribution makeDistribution() {
+ return new ExponentialDistributionImpl(5.0);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {0.00500250166792, 0.0502516792675, 0.126589039921, 0.256466471938,
+ 0.526802578289, 34.5387763949, 23.0258509299, 18.4443972706, 14.9786613678, 11.5129254650};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.1998, 0.198, 0.195, 0.19, 0.18, 0.000200000000000,
+ 0.00200000000002, 0.00499999999997, 0.00999999999994, 0.0199999999999};
+ }
+
+ //------------ Additional tests -------------------------------------------
+
+ public void testCumulativeProbabilityExtremes() throws Exception {
+ setCumulativeTestPoints(new double[] {-2, 0});
+ setCumulativeTestValues(new double[] {0, 0});
+ verifyCumulativeProbabilities();
+ }
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0, 1});
+ setInverseCumulativeTestValues(new double[] {0, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testCumulativeProbability2() throws Exception {
+ double actual = getDistribution().cumulativeProbability(0.25, 0.75);
+ assertEquals(0.0905214, actual, 10e-4);
+ }
+
+ public void testDensity() {
+ ExponentialDistribution d1 = new ExponentialDistributionImpl(1);
+ assertEquals(0.0, d1.density(-1e-9));
+ assertEquals(1.0, d1.density(0.0));
+ assertEquals(0.0, d1.density(1000.0));
+ assertEquals(FastMath.exp(-1), d1.density(1.0));
+ assertEquals(FastMath.exp(-2), d1.density(2.0));
+
+ ExponentialDistribution d2 = new ExponentialDistributionImpl(3);
+ assertEquals(1/3.0, d2.density(0.0));
+ // computed using print(dexp(1, rate=1/3), digits=10) in R 2.5
+ assertEquals(0.2388437702, d2.density(1.0), 1e-8);
+
+ // computed using print(dexp(2, rate=1/3), digits=10) in R 2.5
+ assertEquals(0.1711390397, d2.density(2.0), 1e-8);
+ }
+
+ public void testMeanAccessors() {
+ ExponentialDistribution distribution = (ExponentialDistribution) getDistribution();
+ assertEquals(5d, distribution.getMean(), Double.MIN_VALUE);
+ distribution.setMean(2d);
+ assertEquals(2d, distribution.getMean(), Double.MIN_VALUE);
+ try {
+ distribution.setMean(0);
+ fail("Expecting IllegalArgumentException for 0 mean");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ ExponentialDistributionImpl dist;
+
+ dist = new ExponentialDistributionImpl(11d);
+ assertEquals(dist.getNumericalMean(), 11d, tol);
+ assertEquals(dist.getNumericalVariance(), 11d * 11d, tol);
+
+ dist.setMean(10.5d);
+ assertEquals(dist.getNumericalMean(), 10.5d, tol);
+ assertEquals(dist.getNumericalVariance(), 10.5d * 10.5d, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/FDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/FDistributionTest.java
new file mode 100644
index 0000000..e463415
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/FDistributionTest.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.distribution;
+
+
+/**
+ * Test cases for FDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class FDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for FDistributionTest.
+ * @param name
+ */
+ public FDistributionTest(String name) {
+ super(name);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public FDistribution makeDistribution() {
+ return new FDistributionImpl(5.0, 6.0);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {0.0346808448626, 0.0937009113303, 0.143313661184, 0.202008445998, 0.293728320107,
+ 20.8026639595, 8.74589525602, 5.98756512605, 4.38737418741, 3.10751166664};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.0689156576706, 0.236735653193, 0.364074131941, 0.481570789649, 0.595880479994,
+ 0.000133443915657, 0.00286681303403, 0.00969192007502, 0.0242883861471, 0.0605491314658};
+ }
+
+ // --------------------- Override tolerance --------------
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(1e-9);
+ }
+
+ //---------------------------- Additional test cases -------------------------
+
+ public void testCumulativeProbabilityExtremes() throws Exception {
+ setCumulativeTestPoints(new double[] {-2, 0});
+ setCumulativeTestValues(new double[] {0, 0});
+ verifyCumulativeProbabilities();
+ }
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0, 1});
+ setInverseCumulativeTestValues(new double[] {0, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testDfAccessors() {
+ FDistribution distribution = (FDistribution) getDistribution();
+ assertEquals(5d, distribution.getNumeratorDegreesOfFreedom(), Double.MIN_VALUE);
+ distribution.setNumeratorDegreesOfFreedom(4d);
+ assertEquals(4d, distribution.getNumeratorDegreesOfFreedom(), Double.MIN_VALUE);
+ assertEquals(6d, distribution.getDenominatorDegreesOfFreedom(), Double.MIN_VALUE);
+ distribution.setDenominatorDegreesOfFreedom(4d);
+ assertEquals(4d, distribution.getDenominatorDegreesOfFreedom(), Double.MIN_VALUE);
+ try {
+ distribution.setNumeratorDegreesOfFreedom(0d);
+ fail("Expecting IllegalArgumentException for df = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.setDenominatorDegreesOfFreedom(0d);
+ fail("Expecting IllegalArgumentException for df = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testLargeDegreesOfFreedom() throws Exception {
+ org.apache.commons.math.distribution.FDistributionImpl fd =
+ new org.apache.commons.math.distribution.FDistributionImpl(
+ 100000., 100000.);
+ double p = fd.cumulativeProbability(.999);
+ double x = fd.inverseCumulativeProbability(p);
+ assertEquals(.999, x, 1.0e-5);
+ }
+
+ public void testSmallDegreesOfFreedom() throws Exception {
+ org.apache.commons.math.distribution.FDistributionImpl fd =
+ new org.apache.commons.math.distribution.FDistributionImpl(
+ 1.0, 1.0);
+ double p = fd.cumulativeProbability(0.975);
+ double x = fd.inverseCumulativeProbability(p);
+ assertEquals(0.975, x, 1.0e-5);
+
+ fd.setDenominatorDegreesOfFreedom(2.0);
+ p = fd.cumulativeProbability(0.975);
+ x = fd.inverseCumulativeProbability(p);
+ assertEquals(0.975, x, 1.0e-5);
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ FDistributionImpl dist;
+
+ dist = new FDistributionImpl(1, 2);
+ assertTrue(Double.isNaN(dist.getNumericalMean()));
+ assertTrue(Double.isNaN(dist.getNumericalVariance()));
+
+ dist.setNumeratorDegreesOfFreedom(1);
+ dist.setDenominatorDegreesOfFreedom(3);
+ assertEquals(dist.getNumericalMean(), 3d / (3d - 2d), tol);
+ assertTrue(Double.isNaN(dist.getNumericalVariance()));
+
+ dist.setNumeratorDegreesOfFreedom(1);
+ dist.setDenominatorDegreesOfFreedom(5);
+ assertEquals(dist.getNumericalMean(), 5d / (5d - 2d), tol);
+ assertEquals(dist.getNumericalVariance(), (2d * 5d * 5d * 4d) / 9d, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/GammaDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/GammaDistributionTest.java
new file mode 100644
index 0000000..052f815
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/GammaDistributionTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.distribution;
+
+/**
+ * Test cases for GammaDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class GammaDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for GammaDistributionTest.
+ * @param name
+ */
+ public GammaDistributionTest(String name) {
+ super(name);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public GammaDistribution makeDistribution() {
+ return new GammaDistributionImpl(4d, 2d);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {0.857104827257, 1.64649737269, 2.17973074725, 2.7326367935, 3.48953912565,
+ 26.1244815584, 20.0902350297, 17.5345461395, 15.5073130559, 13.3615661365};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.00427280075546, 0.0204117166709, 0.0362756163658, 0.0542113174239, 0.0773195272491,
+ 0.000394468852816, 0.00366559696761, 0.00874649473311, 0.0166712508128, 0.0311798227954};
+ }
+
+ // --------------------- Override tolerance --------------
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(1e-9);
+ }
+
+ //---------------------------- Additional test cases -------------------------
+ public void testParameterAccessors() {
+ GammaDistribution distribution = (GammaDistribution) getDistribution();
+ assertEquals(4d, distribution.getAlpha(), 0);
+ distribution.setAlpha(3d);
+ assertEquals(3d, distribution.getAlpha(), 0);
+ assertEquals(2d, distribution.getBeta(), 0);
+ distribution.setBeta(4d);
+ assertEquals(4d, distribution.getBeta(), 0);
+ try {
+ distribution.setAlpha(0d);
+ fail("Expecting IllegalArgumentException for alpha = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.setBeta(0d);
+ fail("Expecting IllegalArgumentException for beta = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testProbabilities() throws Exception {
+ testProbability(-1.000, 4.0, 2.0, .0000);
+ testProbability(15.501, 4.0, 2.0, .9499);
+ testProbability(0.504, 4.0, 1.0, .0018);
+ testProbability(10.011, 1.0, 2.0, .9933);
+ testProbability(5.000, 2.0, 2.0, .7127);
+ }
+
+ public void testValues() throws Exception {
+ testValue(15.501, 4.0, 2.0, .9499);
+ testValue(0.504, 4.0, 1.0, .0018);
+ testValue(10.011, 1.0, 2.0, .9933);
+ testValue(5.000, 2.0, 2.0, .7127);
+ }
+
+ private void testProbability(double x, double a, double b, double expected) throws Exception {
+ GammaDistribution distribution = new GammaDistributionImpl( a, b );
+ double actual = distribution.cumulativeProbability(x);
+ assertEquals("probability for " + x, expected, actual, 10e-4);
+ }
+
+ private void testValue(double expected, double a, double b, double p) throws Exception {
+ GammaDistribution distribution = new GammaDistributionImpl( a, b );
+ double actual = distribution.inverseCumulativeProbability(p);
+ assertEquals("critical value for " + p, expected, actual, 10e-4);
+ }
+
+ public void testDensity() {
+ double[] x = new double[]{-0.1, 1e-6, 0.5, 1, 2, 5};
+ // R2.5: print(dgamma(x, shape=1, rate=1), digits=10)
+ checkDensity(1, 1, x, new double[]{0.000000000000, 0.999999000001, 0.606530659713, 0.367879441171, 0.135335283237, 0.006737946999});
+ // R2.5: print(dgamma(x, shape=2, rate=1), digits=10)
+ checkDensity(2, 1, x, new double[]{0.000000000000, 0.000000999999, 0.303265329856, 0.367879441171, 0.270670566473, 0.033689734995});
+ // R2.5: print(dgamma(x, shape=4, rate=1), digits=10)
+ checkDensity(4, 1, x, new double[]{0.000000000e+00, 1.666665000e-19, 1.263605541e-02, 6.131324020e-02, 1.804470443e-01, 1.403738958e-01});
+ // R2.5: print(dgamma(x, shape=4, rate=10), digits=10)
+ checkDensity(4, 10, x, new double[]{0.000000000e+00, 1.666650000e-15, 1.403738958e+00, 7.566654960e-02, 2.748204830e-05, 4.018228850e-17});
+ // R2.5: print(dgamma(x, shape=.1, rate=10), digits=10)
+ checkDensity(0.1, 10, x, new double[]{0.000000000e+00, 3.323953832e+04, 1.663849010e-03, 6.007786726e-06, 1.461647647e-10, 5.996008322e-24});
+ // R2.5: print(dgamma(x, shape=.1, rate=20), digits=10)
+ checkDensity(0.1, 20, x, new double[]{0.000000000e+00, 3.562489883e+04, 1.201557345e-05, 2.923295295e-10, 3.228910843e-19, 1.239484589e-45});
+ // R2.5: print(dgamma(x, shape=.1, rate=4), digits=10)
+ checkDensity(0.1, 4, x, new double[]{0.000000000e+00, 3.032938388e+04, 3.049322494e-02, 2.211502311e-03, 2.170613371e-05, 5.846590589e-11});
+ // R2.5: print(dgamma(x, shape=.1, rate=1), digits=10)
+ checkDensity(0.1, 1, x, new double[]{0.000000000e+00, 2.640334143e+04, 1.189704437e-01, 3.866916944e-02, 7.623306235e-03, 1.663849010e-04});
+ }
+
+ private void checkDensity(double alpha, double rate, double[] x, double[] expected) {
+ GammaDistribution d = new GammaDistributionImpl(alpha, 1 / rate);
+ for (int i = 0; i < x.length; i++) {
+ assertEquals(expected[i], d.density(x[i]), 1e-5);
+ }
+ }
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0, 1});
+ setInverseCumulativeTestValues(new double[] {0, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ GammaDistributionImpl dist;
+
+ dist = new GammaDistributionImpl(1, 2);
+ assertEquals(dist.getNumericalMean(), 2, tol);
+ assertEquals(dist.getNumericalVariance(), 4, tol);
+
+ dist.setAlpha(1.1);
+ dist.setBeta(4.2);
+ assertEquals(dist.getNumericalMean(), 1.1d * 4.2d, tol);
+ assertEquals(dist.getNumericalVariance(), 1.1d * 4.2d * 4.2d, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/HypergeometricDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/HypergeometricDistributionTest.java
new file mode 100644
index 0000000..8522620
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/HypergeometricDistributionTest.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.distribution;
+
+import org.apache.commons.math.TestUtils;
+
+/**
+ * Test cases for HyperGeometriclDistribution.
+ * Extends IntegerDistributionAbstractTest. See class javadoc for
+ * IntegerDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class HypergeometricDistributionTest extends IntegerDistributionAbstractTest {
+
+ /**
+ * Constructor for ChiSquareDistributionTest.
+ * @param name
+ */
+ public HypergeometricDistributionTest(String name) {
+ super(name);
+ }
+
+//-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default discrete distribution instance to use in tests. */
+ @Override
+ public IntegerDistribution makeDistribution() {
+ return new HypergeometricDistributionImpl(10,5, 5);
+ }
+
+ /** Creates the default probability density test input values */
+ @Override
+ public int[] makeDensityTestPoints() {
+ return new int[] {-1, 0, 1, 2, 3, 4, 5, 10};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0d, 0.003968d, 0.099206d, 0.396825d, 0.396825d,
+ 0.099206d, 0.003968d, 0d};
+ }
+
+ /** Creates the default cumulative probability density test input values */
+ @Override
+ public int[] makeCumulativeTestPoints() {
+ return makeDensityTestPoints();
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0d, .003968d, .103175d, .50000d, .896825d, .996032d,
+ 1.00000d, 1d};
+ }
+
+ /** Creates the default inverse cumulative probability test input values */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] {0d, 0.001d, 0.010d, 0.025d, 0.050d, 0.100d, 0.999d,
+ 0.990d, 0.975d, 0.950d, 0.900d, 1d};
+ }
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ @Override
+ public int[] makeInverseCumulativeTestValues() {
+ return new int[] {-1, -1, 0, 0, 0, 0, 4, 3, 3, 3, 3, 5};
+ }
+
+ //-------------------- Additional test cases ------------------------------
+
+ /** Verify that if there are no failures, mass is concentrated on sampleSize */
+ public void testDegenerateNoFailures() throws Exception {
+ setDistribution(new HypergeometricDistributionImpl(5,5,3));
+ setCumulativeTestPoints(new int[] {-1, 0, 1, 3, 10 });
+ setCumulativeTestValues(new double[] {0d, 0d, 0d, 1d, 1d});
+ setDensityTestPoints(new int[] {-1, 0, 1, 3, 10});
+ setDensityTestValues(new double[] {0d, 0d, 0d, 1d, 0d});
+ setInverseCumulativeTestPoints(new double[] {0.1d, 0.5d});
+ setInverseCumulativeTestValues(new int[] {2, 2});
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /** Verify that if there are no successes, mass is concentrated on 0 */
+ public void testDegenerateNoSuccesses() throws Exception {
+ setDistribution(new HypergeometricDistributionImpl(5,0,3));
+ setCumulativeTestPoints(new int[] {-1, 0, 1, 3, 10 });
+ setCumulativeTestValues(new double[] {0d, 1d, 1d, 1d, 1d});
+ setDensityTestPoints(new int[] {-1, 0, 1, 3, 10});
+ setDensityTestValues(new double[] {0d, 1d, 0d, 0d, 0d});
+ setInverseCumulativeTestPoints(new double[] {0.1d, 0.5d});
+ setInverseCumulativeTestValues(new int[] {-1, -1});
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /** Verify that if sampleSize = populationSize, mass is concentrated on numberOfSuccesses */
+ public void testDegenerateFullSample() throws Exception {
+ setDistribution(new HypergeometricDistributionImpl(5,3,5));
+ setCumulativeTestPoints(new int[] {-1, 0, 1, 3, 10 });
+ setCumulativeTestValues(new double[] {0d, 0d, 0d, 1d, 1d});
+ setDensityTestPoints(new int[] {-1, 0, 1, 3, 10});
+ setDensityTestValues(new double[] {0d, 0d, 0d, 1d, 0d});
+ setInverseCumulativeTestPoints(new double[] {0.1d, 0.5d});
+ setInverseCumulativeTestValues(new int[] {2, 2});
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testPopulationSize() {
+ HypergeometricDistribution dist = new HypergeometricDistributionImpl(5,3,5);
+ try {
+ dist.setPopulationSize(-1);
+ fail("negative population size. IllegalArgumentException expected");
+ } catch(IllegalArgumentException ex) {
+ }
+
+ dist.setPopulationSize(10);
+ assertEquals(10, dist.getPopulationSize());
+ }
+
+ public void testLargeValues() {
+ int populationSize = 3456;
+ int sampleSize = 789;
+ int numberOfSucceses = 101;
+ double[][] data = {
+ {0.0, 2.75646034603961e-12, 2.75646034603961e-12, 1.0},
+ {1.0, 8.55705370142386e-11, 8.83269973602783e-11, 0.999999999997244},
+ {2.0, 1.31288129219665e-9, 1.40120828955693e-9, 0.999999999911673},
+ {3.0, 1.32724172984193e-8, 1.46736255879763e-8, 0.999999998598792},
+ {4.0, 9.94501711734089e-8, 1.14123796761385e-7, 0.999999985326375},
+ {5.0, 5.89080768883643e-7, 7.03204565645028e-7, 0.999999885876203},
+ {20.0, 0.0760051397707708, 0.27349758476299, 0.802507555007781},
+ {21.0, 0.087144222047629, 0.360641806810619, 0.72650241523701},
+ {22.0, 0.0940378846881819, 0.454679691498801, 0.639358193189381},
+ {23.0, 0.0956897500614809, 0.550369441560282, 0.545320308501199},
+ {24.0, 0.0919766921922999, 0.642346133752582, 0.449630558439718},
+ {25.0, 0.083641637261095, 0.725987771013677, 0.357653866247418},
+ {96.0, 5.93849188852098e-57, 1.0, 6.01900244560712e-57},
+ {97.0, 7.96593036832547e-59, 1.0, 8.05105570861321e-59},
+ {98.0, 8.44582921934367e-61, 1.0, 8.5125340287733e-61},
+ {99.0, 6.63604297068222e-63, 1.0, 6.670480942963e-63},
+ {100.0, 3.43501099007557e-65, 1.0, 3.4437972280786e-65},
+ {101.0, 8.78623800302957e-68, 1.0, 8.78623800302957e-68},
+ };
+
+ testHypergeometricDistributionProbabilities(populationSize, sampleSize, numberOfSucceses, data);
+ }
+
+ private void testHypergeometricDistributionProbabilities(int populationSize, int sampleSize, int numberOfSucceses, double[][] data) {
+ HypergeometricDistributionImpl dist = new HypergeometricDistributionImpl(populationSize, numberOfSucceses, sampleSize);
+ for (int i = 0; i < data.length; ++i) {
+ int x = (int)data[i][0];
+ double pdf = data[i][1];
+ double actualPdf = dist.probability(x);
+ TestUtils.assertRelativelyEquals("Expected equals for <"+x+"> pdf",pdf, actualPdf, 1.0e-9);
+
+ double cdf = data[i][2];
+ double actualCdf = dist.cumulativeProbability(x);
+ TestUtils.assertRelativelyEquals("Expected equals for <"+x+"> cdf",cdf, actualCdf, 1.0e-9);
+
+ double cdf1 = data[i][3];
+ double actualCdf1 = dist.upperCumulativeProbability(x);
+ TestUtils.assertRelativelyEquals("Expected equals for <"+x+"> cdf1",cdf1, actualCdf1, 1.0e-9);
+ }
+ }
+
+ public void testMoreLargeValues() {
+ int populationSize = 26896;
+ int sampleSize = 895;
+ int numberOfSucceses = 55;
+ double[][] data = {
+ {0.0, 0.155168304750504, 0.155168304750504, 1.0},
+ {1.0, 0.29437545000746, 0.449543754757964, 0.844831695249496},
+ {2.0, 0.273841321577003, 0.723385076334967, 0.550456245242036},
+ {3.0, 0.166488572570786, 0.889873648905753, 0.276614923665033},
+ {4.0, 0.0743969744713231, 0.964270623377076, 0.110126351094247},
+ {5.0, 0.0260542785784855, 0.990324901955562, 0.0357293766229237},
+ {20.0, 3.57101101678792e-16, 1.0, 3.78252101622096e-16},
+ {21.0, 2.00551638598312e-17, 1.0, 2.11509999433041e-17},
+ {22.0, 1.04317070180562e-18, 1.0, 1.09583608347287e-18},
+ {23.0, 5.03153504903308e-20, 1.0, 5.266538166725e-20},
+ {24.0, 2.2525984149695e-21, 1.0, 2.35003117691919e-21},
+ {25.0, 9.3677424515947e-23, 1.0, 9.74327619496943e-23},
+ {50.0, 9.83633962945521e-69, 1.0, 9.8677629437617e-69},
+ {51.0, 3.13448949497553e-71, 1.0, 3.14233143064882e-71},
+ {52.0, 7.82755221928122e-74, 1.0, 7.84193567329055e-74},
+ {53.0, 1.43662126065532e-76, 1.0, 1.43834540093295e-76},
+ {54.0, 1.72312692517348e-79, 1.0, 1.7241402776278e-79},
+ {55.0, 1.01335245432581e-82, 1.0, 1.01335245432581e-82},
+ };
+ testHypergeometricDistributionProbabilities(populationSize, sampleSize, numberOfSucceses, data);
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ HypergeometricDistributionImpl dist;
+
+ dist = new HypergeometricDistributionImpl(1500, 40, 100);
+ assertEquals(dist.getNumericalMean(), 40d * 100d / 1500d, tol);
+ assertEquals(dist.getNumericalVariance(), ( 100d * 40d * (1500d - 100d) * (1500d - 40d) ) / ( (1500d * 1500d * 1499d) ), tol);
+
+ dist.setPopulationSize(3000);
+ dist.setNumberOfSuccesses(55);
+ dist.setSampleSize(200);
+ assertEquals(dist.getNumericalMean(), 55d * 200d / 3000d, tol);
+ assertEquals(dist.getNumericalVariance(), ( 200d * 55d * (3000d - 200d) * (3000d - 55d) ) / ( (3000d * 3000d * 2999d) ), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java b/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java
new file mode 100644
index 0000000..aee205c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.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.distribution;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * Abstract base class for {@link IntegerDistribution} tests.
+ * <p>
+ * To create a concrete test class for an integer distribution implementation,
+ * implement makeDistribution() to return a distribution instance to use in
+ * tests and each of the test data generation methods below. In each case, the
+ * test points and test values arrays returned represent parallel arrays of
+ * inputs and expected values for the distribution returned by makeDistribution().
+ * <p>
+ * makeDensityTestPoints() -- arguments used to test probability density calculation
+ * makeDensityTestValues() -- expected probability densities
+ * makeCumulativeTestPoints() -- arguments used to test cumulative probabilities
+ * makeCumulativeTestValues() -- expected cumulative probabilites
+ * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf evaluation
+ * makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * <p>
+ * To implement additional test cases with different distribution instances and test data,
+ * use the setXxx methods for the instance data in test cases and call the verifyXxx methods
+ * to verify results.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public abstract class IntegerDistributionAbstractTest extends TestCase {
+
+//-------------------- Private test instance data -------------------------
+ /** Discrete distribution instance used to perform tests */
+ private IntegerDistribution distribution;
+
+ /** Tolerance used in comparing expected and returned values */
+ private double tolerance = 1E-4;
+
+ /** Arguments used to test probability density calculations */
+ private int[] densityTestPoints;
+
+ /** Values used to test probability density calculations */
+ private double[] densityTestValues;
+
+ /** Arguments used to test cumulative probability density calculations */
+ private int[] cumulativeTestPoints;
+
+ /** Values used to test cumulative probability density calculations */
+ private double[] cumulativeTestValues;
+
+ /** Arguments used to test inverse cumulative probability density calculations */
+ private double[] inverseCumulativeTestPoints;
+
+ /** Values used to test inverse cumulative probability density calculations */
+ private int[] inverseCumulativeTestValues;
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * Constructor for IntegerDistributionAbstractTest.
+ * @param name
+ */
+ public IntegerDistributionAbstractTest(String name) {
+ super(name);
+ }
+
+ //-------------------- Abstract methods -----------------------------------
+
+ /** Creates the default discrete distribution instance to use in tests. */
+ public abstract IntegerDistribution makeDistribution();
+
+ /** Creates the default probability density test input values */
+ public abstract int[] makeDensityTestPoints();
+
+ /** Creates the default probability density test expected values */
+ public abstract double[] makeDensityTestValues();
+
+ /** Creates the default cumulative probability density test input values */
+ public abstract int[] makeCumulativeTestPoints();
+
+ /** Creates the default cumulative probability density test expected values */
+ public abstract double[] makeCumulativeTestValues();
+
+ /** Creates the default inverse cumulative probability test input values */
+ public abstract double[] makeInverseCumulativeTestPoints();
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ public abstract int[] makeInverseCumulativeTestValues();
+
+ //-------------------- Setup / tear down ----------------------------------
+
+ /**
+ * Setup sets all test instance data to default values
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ distribution = makeDistribution();
+ densityTestPoints = makeDensityTestPoints();
+ densityTestValues = makeDensityTestValues();
+ cumulativeTestPoints = makeCumulativeTestPoints();
+ cumulativeTestValues = makeCumulativeTestValues();
+ inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
+ inverseCumulativeTestValues = makeInverseCumulativeTestValues();
+ }
+
+ /**
+ * Cleans up test instance data
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ distribution = null;
+ densityTestPoints = null;
+ densityTestValues = null;
+ cumulativeTestPoints = null;
+ cumulativeTestValues = null;
+ inverseCumulativeTestPoints = null;
+ inverseCumulativeTestValues = null;
+ }
+
+ //-------------------- Verification methods -------------------------------
+
+ /**
+ * Verifies that probability density calculations match expected values
+ * using current test instance data
+ */
+ protected void verifyDensities() throws Exception {
+ for (int i = 0; i < densityTestPoints.length; i++) {
+ assertEquals("Incorrect density value returned for " + densityTestPoints[i],
+ densityTestValues[i],
+ distribution.probability(densityTestPoints[i]), tolerance);
+ }
+ }
+
+ /**
+ * Verifies that cumulative probability density calculations match expected values
+ * using current test instance data
+ */
+ protected void verifyCumulativeProbabilities() throws Exception {
+ for (int i = 0; i < cumulativeTestPoints.length; i++) {
+ assertEquals("Incorrect cumulative probability value returned for " + cumulativeTestPoints[i],
+ cumulativeTestValues[i],
+ distribution.cumulativeProbability(cumulativeTestPoints[i]), tolerance);
+ }
+ }
+
+
+ /**
+ * Verifies that inverse cumulative probability density calculations match expected values
+ * using current test instance data
+ */
+ protected void verifyInverseCumulativeProbabilities() throws Exception {
+ for (int i = 0; i < inverseCumulativeTestPoints.length; i++) {
+ assertEquals("Incorrect inverse cumulative probability value returned for "
+ + inverseCumulativeTestPoints[i], inverseCumulativeTestValues[i],
+ distribution.inverseCumulativeProbability(inverseCumulativeTestPoints[i]));
+ }
+ }
+
+ //------------------------ Default test cases -----------------------------
+
+ /**
+ * Verifies that probability density calculations match expected values
+ * using default test instance data
+ */
+ public void testDensities() throws Exception {
+ verifyDensities();
+ }
+
+ /**
+ * Verifies that cumulative probability density calculations match expected values
+ * using default test instance data
+ */
+ public void testCumulativeProbabilities() throws Exception {
+ verifyCumulativeProbabilities();
+ }
+
+ /**
+ * Verifies that floating point arguments are correctly handled by
+ * cumulativeProbablility(-,-)
+ * JIRA: MATH-184
+ */
+ public void testFloatingPointArguments() throws Exception {
+ for (int i = 0; i < cumulativeTestPoints.length; i++) {
+ double arg = cumulativeTestPoints[i];
+ assertEquals(
+ "Incorrect cumulative probability value returned for " +
+ cumulativeTestPoints[i],
+ cumulativeTestValues[i],
+ distribution.cumulativeProbability(arg), tolerance);
+ if (i < cumulativeTestPoints.length - 1) {
+ double arg2 = cumulativeTestPoints[i + 1];
+ assertEquals("Inconsistent probability for discrete range " +
+ "[ " + arg + "," + arg2 + " ]",
+ distribution.cumulativeProbability(
+ cumulativeTestPoints[i],
+ cumulativeTestPoints[i + 1]),
+ distribution.cumulativeProbability(arg, arg2), tolerance);
+ arg = arg - FastMath.random();
+ arg2 = arg2 + FastMath.random();
+ assertEquals("Inconsistent probability for discrete range " +
+ "[ " + arg + "," + arg2 + " ]",
+ distribution.cumulativeProbability(
+ cumulativeTestPoints[i],
+ cumulativeTestPoints[i + 1]),
+ distribution.cumulativeProbability(arg, arg2), tolerance);
+ }
+ }
+ int one = 1;
+ int ten = 10;
+ int two = 2;
+ double oned = one;
+ double twod = two;
+ double tend = ten;
+ assertEquals(distribution.cumulativeProbability(one, two),
+ distribution.cumulativeProbability(oned, twod), tolerance);
+ assertEquals(distribution.cumulativeProbability(one, two),
+ distribution.cumulativeProbability(oned - tolerance,
+ twod + 0.9), tolerance);
+ assertEquals(distribution.cumulativeProbability(two, ten),
+ distribution.cumulativeProbability(twod, tend), tolerance);
+ assertEquals(distribution.cumulativeProbability(two, ten),
+ distribution.cumulativeProbability(twod - tolerance,
+ tend + 0.9), tolerance);
+ }
+
+ /**
+ * Verifies that inverse cumulative probability density calculations match expected values
+ * using default test instance data
+ */
+ public void testInverseCumulativeProbabilities() throws Exception {
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /**
+ * Verifies that illegal arguments are correctly handled
+ */
+ public void testIllegalArguments() throws Exception {
+ try {
+ distribution.cumulativeProbability(1, 0);
+ fail("Expecting IllegalArgumentException for bad cumulativeProbability interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.inverseCumulativeProbability(-1);
+ fail("Expecting IllegalArgumentException for p = -1");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ distribution.inverseCumulativeProbability(2);
+ fail("Expecting IllegalArgumentException for p = 2");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Test sampling
+ */
+ public void testSampling() throws Exception {
+ int[] densityPoints = makeDensityTestPoints();
+ double[] densityValues = makeDensityTestValues();
+ int sampleSize = 1000;
+ int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
+ AbstractIntegerDistribution distribution = (AbstractIntegerDistribution) makeDistribution();
+ double[] expectedCounts = new double[length];
+ long[] observedCounts = new long[length];
+ for (int i = 0; i < length; i++) {
+ expectedCounts[i] = sampleSize * densityValues[i];
+ }
+ distribution.reseedRandomGenerator(1000); // Use fixed seed
+ int[] sample = distribution.sample(sampleSize);
+ for (int i = 0; i < sampleSize; i++) {
+ for (int j = 0; j < length; j++) {
+ if (sample[i] == densityPoints[j]) {
+ observedCounts[j]++;
+ }
+ }
+ }
+ TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
+ }
+
+ //------------------ Getters / Setters for test instance data -----------
+ /**
+ * @return Returns the cumulativeTestPoints.
+ */
+ protected int[] getCumulativeTestPoints() {
+ return cumulativeTestPoints;
+ }
+
+ /**
+ * @param cumulativeTestPoints The cumulativeTestPoints to set.
+ */
+ protected void setCumulativeTestPoints(int[] cumulativeTestPoints) {
+ this.cumulativeTestPoints = cumulativeTestPoints;
+ }
+
+ /**
+ * @return Returns the cumulativeTestValues.
+ */
+ protected double[] getCumulativeTestValues() {
+ return cumulativeTestValues;
+ }
+
+ /**
+ * @param cumulativeTestValues The cumulativeTestValues to set.
+ */
+ protected void setCumulativeTestValues(double[] cumulativeTestValues) {
+ this.cumulativeTestValues = cumulativeTestValues;
+ }
+
+ /**
+ * @return Returns the densityTestPoints.
+ */
+ protected int[] getDensityTestPoints() {
+ return densityTestPoints;
+ }
+
+ /**
+ * @param densityTestPoints The densityTestPoints to set.
+ */
+ protected void setDensityTestPoints(int[] densityTestPoints) {
+ this.densityTestPoints = densityTestPoints;
+ }
+
+ /**
+ * @return Returns the densityTestValues.
+ */
+ protected double[] getDensityTestValues() {
+ return densityTestValues;
+ }
+
+ /**
+ * @param densityTestValues The densityTestValues to set.
+ */
+ protected void setDensityTestValues(double[] densityTestValues) {
+ this.densityTestValues = densityTestValues;
+ }
+
+ /**
+ * @return Returns the distribution.
+ */
+ protected IntegerDistribution getDistribution() {
+ return distribution;
+ }
+
+ /**
+ * @param distribution The distribution to set.
+ */
+ protected void setDistribution(IntegerDistribution distribution) {
+ this.distribution = distribution;
+ }
+
+ /**
+ * @return Returns the inverseCumulativeTestPoints.
+ */
+ protected double[] getInverseCumulativeTestPoints() {
+ return inverseCumulativeTestPoints;
+ }
+
+ /**
+ * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to set.
+ */
+ protected void setInverseCumulativeTestPoints(double[] inverseCumulativeTestPoints) {
+ this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
+ }
+
+ /**
+ * @return Returns the inverseCumulativeTestValues.
+ */
+ protected int[] getInverseCumulativeTestValues() {
+ return inverseCumulativeTestValues;
+ }
+
+ /**
+ * @param inverseCumulativeTestValues The inverseCumulativeTestValues to set.
+ */
+ protected void setInverseCumulativeTestValues(int[] inverseCumulativeTestValues) {
+ this.inverseCumulativeTestValues = inverseCumulativeTestValues;
+ }
+
+ /**
+ * @return Returns the tolerance.
+ */
+ protected double getTolerance() {
+ return tolerance;
+ }
+
+ /**
+ * @param tolerance The tolerance to set.
+ */
+ protected void setTolerance(double tolerance) {
+ this.tolerance = tolerance;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java
new file mode 100644
index 0000000..d45c0dc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.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.distribution;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for NormalDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class NormalDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for NormalDistributionTest.
+ * @param arg0
+ */
+ public NormalDistributionTest(String arg0) {
+ super(arg0);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public NormalDistribution makeDistribution() {
+ return new NormalDistributionImpl(2.1, 1.4);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R
+ return new double[] {-2.226325228634938d, -1.156887023657177d, -0.643949578356075d, -0.2027950777320613d, 0.305827808237559d,
+ 6.42632522863494d, 5.35688702365718d, 4.843949578356074d, 4.40279507773206d, 3.89417219176244d};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001d, 0.01d, 0.025d, 0.05d, 0.1d, 0.999d,
+ 0.990d, 0.975d, 0.950d, 0.900d};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.00240506434076, 0.0190372444310, 0.0417464784322, 0.0736683145538, 0.125355951380,
+ 0.00240506434076, 0.0190372444310, 0.0417464784322, 0.0736683145538, 0.125355951380};
+ }
+
+ // --------------------- Override tolerance --------------
+ protected double defaultTolerance = NormalDistributionImpl.DEFAULT_INVERSE_ABSOLUTE_ACCURACY;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(defaultTolerance);
+ }
+
+ //---------------------------- Additional test cases -------------------------
+
+ private void verifyQuantiles() throws Exception {
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ double mu = distribution.getMean();
+ double sigma = distribution.getStandardDeviation();
+ setCumulativeTestPoints( new double[] {mu - 2 *sigma, mu - sigma,
+ mu, mu + sigma, mu + 2 * sigma, mu + 3 * sigma, mu + 4 * sigma,
+ mu + 5 * sigma});
+ // Quantiles computed using R (same as Mathematica)
+ setCumulativeTestValues(new double[] {0.02275013194817921, 0.158655253931457, 0.5, 0.841344746068543,
+ 0.977249868051821, 0.99865010196837, 0.999968328758167, 0.999999713348428});
+ verifyCumulativeProbabilities();
+ }
+
+ public void testQuantiles() throws Exception {
+ setDensityTestValues(new double[] {0.0385649760808, 0.172836231799, 0.284958771715, 0.172836231799, 0.0385649760808,
+ 0.00316560600853, 9.55930184035e-05, 1.06194251052e-06});
+ verifyQuantiles();
+ verifyDensities();
+
+ setDistribution(new NormalDistributionImpl(0, 1));
+ setDensityTestValues(new double[] {0.0539909665132, 0.241970724519, 0.398942280401, 0.241970724519, 0.0539909665132,
+ 0.00443184841194, 0.000133830225765, 1.48671951473e-06});
+ verifyQuantiles();
+ verifyDensities();
+
+ setDistribution(new NormalDistributionImpl(0, 0.1));
+ setDensityTestValues(new double[] {0.539909665132, 2.41970724519, 3.98942280401, 2.41970724519,
+ 0.539909665132, 0.0443184841194, 0.00133830225765, 1.48671951473e-05});
+ verifyQuantiles();
+ verifyDensities();
+ }
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0, 1});
+ setInverseCumulativeTestValues(
+ new double[] {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testGetMean() {
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ assertEquals(2.1, distribution.getMean(), 0);
+ }
+
+ public void testSetMean() throws Exception {
+ double mu = FastMath.random();
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ distribution.setMean(mu);
+ verifyQuantiles();
+ }
+
+ public void testGetStandardDeviation() {
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ assertEquals(1.4, distribution.getStandardDeviation(), 0);
+ }
+
+ public void testSetStandardDeviation() throws Exception {
+ double sigma = 0.1d + FastMath.random();
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ distribution.setStandardDeviation(sigma);
+ assertEquals(sigma, distribution.getStandardDeviation(), 0);
+ verifyQuantiles();
+ try {
+ distribution.setStandardDeviation(0);
+ fail("Expecting IllegalArgumentException for sd = 0");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ public void testDensity() {
+ double [] x = new double[]{-2, -1, 0, 1, 2};
+ // R 2.5: print(dnorm(c(-2,-1,0,1,2)), digits=10)
+ checkDensity(0, 1, x, new double[]{0.05399096651, 0.24197072452, 0.39894228040, 0.24197072452, 0.05399096651});
+ // R 2.5: print(dnorm(c(-2,-1,0,1,2), mean=1.1), digits=10)
+ checkDensity(1.1, 1, x, new double[]{0.003266819056,0.043983595980,0.217852177033,0.396952547477,0.266085249899});
+ }
+
+ private void checkDensity(double mean, double sd, double[] x, double[] expected) {
+ NormalDistribution d = new NormalDistributionImpl(mean, sd);
+ for (int i = 0; i < x.length; i++) {
+ assertEquals(expected[i], d.density(x[i]), 1e-9);
+ }
+ }
+
+ /**
+ * Check to make sure top-coding of extreme values works correctly.
+ * Verifies fixes for JIRA MATH-167, MATH-414
+ */
+ public void testExtremeValues() throws Exception {
+ NormalDistribution distribution = (NormalDistribution) getDistribution();
+ distribution.setMean(0);
+ distribution.setStandardDeviation(1);
+ for (int i = 0; i < 100; i++) { // make sure no convergence exception
+ double lowerTail = distribution.cumulativeProbability(-i);
+ double upperTail = distribution.cumulativeProbability(i);
+ if (i < 9) { // make sure not top-coded
+ // For i = 10, due to bad tail precision in erf (MATH-364), 1 is returned
+ // TODO: once MATH-364 is resolved, replace 9 with 30
+ assertTrue(lowerTail > 0.0d);
+ assertTrue(upperTail < 1.0d);
+ }
+ else { // make sure top coding not reversed
+ assertTrue(lowerTail < 0.00001);
+ assertTrue(upperTail > 0.99999);
+ }
+ }
+
+ assertEquals(distribution.cumulativeProbability(Double.MAX_VALUE), 1, 0);
+ assertEquals(distribution.cumulativeProbability(-Double.MAX_VALUE), 0, 0);
+ assertEquals(distribution.cumulativeProbability(Double.POSITIVE_INFINITY), 1, 0);
+ assertEquals(distribution.cumulativeProbability(Double.NEGATIVE_INFINITY), 0, 0);
+
+ }
+
+ public void testMath280() throws MathException {
+ NormalDistribution normal = new NormalDistributionImpl(0,1);
+ double result = normal.inverseCumulativeProbability(0.9986501019683698);
+ assertEquals(3.0, result, defaultTolerance);
+ result = normal.inverseCumulativeProbability(0.841344746068543);
+ assertEquals(1.0, result, defaultTolerance);
+ result = normal.inverseCumulativeProbability(0.9999683287581673);
+ assertEquals(4.0, result, defaultTolerance);
+ result = normal.inverseCumulativeProbability(0.9772498680518209);
+ assertEquals(2.0, result, defaultTolerance);
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ NormalDistributionImpl dist;
+
+ dist = new NormalDistributionImpl(0, 1);
+ assertEquals(dist.getNumericalVariance(), 1, tol);
+
+ dist.setMean(2.2);
+ dist.setStandardDeviation(1.4);
+ assertEquals(dist.getNumericalVariance(), 1.4 * 1.4, tol);
+
+ dist.setMean(-2000.9);
+ dist.setStandardDeviation(10.4);
+ assertEquals(dist.getNumericalVariance(), 10.4 * 10.4, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/PascalDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/PascalDistributionTest.java
new file mode 100644
index 0000000..e476e78
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/PascalDistributionTest.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.distribution;
+
+/**
+ * Test cases for PascalDistribution.
+ * Extends IntegerDistributionAbstractTest. See class javadoc for
+ * IntegerDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class PascalDistributionTest extends IntegerDistributionAbstractTest {
+
+ /**
+ * Constructor for PascalDistributionTest.
+ * @param name
+ */
+ public PascalDistributionTest(String name) {
+ super(name);
+ }
+
+ // --------------------- Override tolerance --------------
+ protected double defaultTolerance = NormalDistributionImpl.DEFAULT_INVERSE_ABSOLUTE_ACCURACY;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(defaultTolerance);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default discrete distribution instance to use in tests. */
+ @Override
+ public IntegerDistribution makeDistribution() {
+ return new PascalDistributionImpl(10,0.70);
+ }
+
+ /** Creates the default probability density test input values */
+ @Override
+ public int[] makeDensityTestPoints() {
+ return new int[] {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0, 0.0282475249, 0.0847425747, 0.139825248255, 0.167790297906, 0.163595540458,
+ 0.137420253985, 0.103065190489, 0.070673273478, 0.0450542118422, 0.0270325271053,
+ 0.0154085404500, 0.0084046584273};
+ }
+
+ /** Creates the default cumulative probability density test input values */
+ @Override
+ public int[] makeCumulativeTestPoints() {
+ return makeDensityTestPoints();
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0, 0.0282475249, 0.1129900996, 0.252815347855, 0.420605645761, 0.584201186219,
+ 0.721621440204, 0.824686630693, 0.895359904171, 0.940414116013, 0.967446643119,
+ 0.982855183569, 0.991259841996};
+ }
+
+ /** Creates the default inverse cumulative probability test input values */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] {0, 0.001d, 0.010d, 0.025d, 0.050d, 0.100d, 0.999d,
+ 0.990d, 0.975d, 0.950d, 0.900d, 1};
+ }
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ @Override
+ public int[] makeInverseCumulativeTestValues() {
+ return new int[] {-1, -1, -1, -1, 0, 0, 13, 10, 9, 8, 7, Integer.MAX_VALUE};
+ }
+
+ //----------------- Additional test cases ---------------------------------
+
+ /** Test degenerate case p = 0 */
+ public void testDegenerate0() throws Exception {
+ setDistribution(new PascalDistributionImpl(5,0.0d));
+ setCumulativeTestPoints(new int[] {-1, 0, 1, 5, 10 });
+ setCumulativeTestValues(new double[] {0d, 0d, 0d, 0d, 0d});
+ setDensityTestPoints(new int[] {-1, 0, 1, 10, 11});
+ setDensityTestValues(new double[] {0d, 0d, 0d, 0d, 0d});
+ setInverseCumulativeTestPoints(new double[] {0.1d, 0.5d});
+ setInverseCumulativeTestValues(new int[] {Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1});
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ /** Test degenerate case p = 1 */
+ public void testDegenerate1() throws Exception {
+ setDistribution(new PascalDistributionImpl(5,1.0d));
+ setCumulativeTestPoints(new int[] {-1, 0, 1, 2, 5, 10 });
+ setCumulativeTestValues(new double[] {0d, 1d, 1d, 1d, 1d, 1d});
+ setDensityTestPoints(new int[] {-1, 0, 1, 2, 5, 10});
+ setDensityTestValues(new double[] {0d, 1d, 0d, 0d, 0d, 0d});
+ setInverseCumulativeTestPoints(new double[] {0.1d, 0.5d});
+ setInverseCumulativeTestValues(new int[] {-1, -1});
+ verifyDensities();
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ PascalDistributionImpl dist;
+
+ dist = new PascalDistributionImpl(10, 0.5);
+ assertEquals(dist.getNumericalMean(), ( 10d * 0.5d ) / 0.5d, tol);
+ assertEquals(dist.getNumericalVariance(), ( 10d * 0.5d ) / (0.5d * 0.5d), tol);
+
+ dist.setNumberOfSuccesses(25);
+ dist.setProbabilityOfSuccess(0.3);
+ assertEquals(dist.getNumericalMean(), ( 25d * 0.3d ) / 0.7d, tol);
+ assertEquals(dist.getNumericalVariance(), ( 25d * 0.3d ) / (0.7d * 0.7d), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/PoissonDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/PoissonDistributionTest.java
new file mode 100644
index 0000000..1d2bc35
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/PoissonDistributionTest.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 org.apache.commons.math.MathException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * <code>PoissonDistributionTest</code>
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class PoissonDistributionTest extends IntegerDistributionAbstractTest {
+
+ /**
+ * Poisson parameter value for the test distribution.
+ */
+ private static final double DEFAULT_TEST_POISSON_PARAMETER = 4.0;
+
+ /**
+ * Constructor.
+ * @param name
+ */
+ public PoissonDistributionTest(String name) {
+ super(name);
+ setTolerance(1e-12);
+ }
+
+ /**
+ * Creates the default discrete distribution instance to use in tests.
+ */
+ @Override
+ public IntegerDistribution makeDistribution() {
+ return new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
+ }
+
+ /**
+ * Creates the default probability density test input values.
+ */
+ @Override
+ public int[] makeDensityTestPoints() {
+ return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20};
+ }
+
+ /**
+ * Creates the default probability density test expected values.
+ * These and all other test values are generated by R, version 1.8.1
+ */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] { 0d, 0.0183156388887d, 0.073262555555d,
+ 0.14652511111d, 0.195366814813d, 0.195366814813,
+ 0.156293451851d, 0.00529247667642d, 8.27746364655e-09};
+ }
+
+ /**
+ * Creates the default cumulative probability density test input values.
+ */
+ @Override
+ public int[] makeCumulativeTestPoints() {
+ return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20 };
+ }
+
+ /**
+ * Creates the default cumulative probability density test expected values.
+ */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] { 0d, 0.0183156388887d, 0.0915781944437d,
+ 0.238103305554d, 0.433470120367d, 0.62883693518,
+ 0.78513038703d, 0.99716023388d, 0.999999998077 };
+ }
+
+ /**
+ * Creates the default inverse cumulative probability test input values.
+ * Increased 3rd and 7th values slightly as computed cumulative
+ * probabilities for corresponding values exceeds the target value (still
+ * within tolerance).
+ */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] { 0d, 0.018315638889d, 0.0915781944437d,
+ 0.238103305554d, 0.433470120367d, 0.62883693518,
+ 0.78513038704d, 0.99716023388d, 0.999999998077 };
+ }
+
+ /**
+ * Creates the default inverse cumulative probability density test expected values.
+ */
+ @Override
+ public int[] makeInverseCumulativeTestValues() {
+ return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20};
+ }
+
+ /**
+ * Test the normal approximation of the Poisson distribution by
+ * calculating P(90 ≤ X ≤ 110) for X = Po(100) and
+ * P(9900 ≤ X ≤ 10200) for X = Po(10000)
+ */
+ public void testNormalApproximateProbability() throws Exception {
+ PoissonDistribution dist = new PoissonDistributionImpl(100);
+ double result = dist.normalApproximateProbability(110)
+ - dist.normalApproximateProbability(89);
+ assertEquals(0.706281887248, result, 1E-10);
+ dist.setMean(10000);
+ result = dist.normalApproximateProbability(10200)
+ - dist.normalApproximateProbability(9899);
+ assertEquals(0.820070051552, result, 1E-10);
+ }
+
+ /**
+ * Test the degenerate cases of a 0.0 and 1.0 inverse cumulative probability.
+ * @throws Exception
+ */
+ public void testDegenerateInverseCumulativeProbability() throws Exception {
+ PoissonDistribution dist = new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
+ assertEquals(Integer.MAX_VALUE, dist.inverseCumulativeProbability(1.0d));
+ assertEquals(-1, dist.inverseCumulativeProbability(0d));
+ }
+
+ public void testMean() {
+ PoissonDistribution dist = new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
+ try {
+ dist.setMean(-1);
+ fail("negative mean. IllegalArgumentException expected");
+ } catch(IllegalArgumentException ex) {
+ }
+
+ dist.setMean(10.0);
+ assertEquals(10.0, dist.getMean(), 0.0);
+ }
+
+ public void testLargeMeanCumulativeProbability() {
+ PoissonDistribution dist = new PoissonDistributionImpl(1.0);
+ double mean = 1.0;
+ while (mean <= 10000000.0) {
+ dist.setMean(mean);
+
+ double x = mean * 2.0;
+ double dx = x / 10.0;
+ double p = Double.NaN;
+ double sigma = FastMath.sqrt(mean);
+ while (x >= 0) {
+ try {
+ p = dist.cumulativeProbability(x);
+ assertFalse("NaN cumulative probability returned for mean = " +
+ mean + " x = " + x,Double.isNaN(p));
+ if (x > mean - 2 * sigma) {
+ assertTrue("Zero cum probaility returned for mean = " +
+ mean + " x = " + x, p > 0);
+ }
+ } catch (MathException ex) {
+ fail("mean of " + mean + " and x of " + x + " caused " + ex.getMessage());
+ }
+ x -= dx;
+ }
+
+ mean *= 10.0;
+ }
+ }
+
+ /**
+ * JIRA: MATH-282
+ */
+ public void testCumulativeProbabilitySpecial() throws Exception {
+ PoissonDistribution dist = new PoissonDistributionImpl(1.0);
+ dist.setMean(9120);
+ checkProbability(dist, 9075);
+ checkProbability(dist, 9102);
+ dist.setMean(5058);
+ checkProbability(dist, 5044);
+ dist.setMean(6986);
+ checkProbability(dist, 6950);
+ }
+
+ private void checkProbability(PoissonDistribution dist, double x) throws Exception {
+ double p = dist.cumulativeProbability(x);
+ assertFalse("NaN cumulative probability returned for mean = " +
+ dist.getMean() + " x = " + x, Double.isNaN(p));
+ assertTrue("Zero cum probability returned for mean = " +
+ dist.getMean() + " x = " + x, p > 0);
+ }
+
+ public void testLargeMeanInverseCumulativeProbability() throws Exception {
+ PoissonDistribution dist = new PoissonDistributionImpl(1.0);
+ double mean = 1.0;
+ while (mean <= 100000.0) { // Extended test value: 1E7. Reduced to limit run time.
+ dist.setMean(mean);
+ double p = 0.1;
+ double dp = p;
+ while (p < .99) {
+ double ret = Double.NaN;
+ try {
+ ret = dist.inverseCumulativeProbability(p);
+ // Verify that returned value satisties definition
+ assertTrue(p >= dist.cumulativeProbability(ret));
+ assertTrue(p < dist.cumulativeProbability(ret + 1));
+ } catch (MathException ex) {
+ fail("mean of " + mean + " and p of " + p + " caused " + ex.getMessage());
+ }
+ p += dp;
+ }
+ mean *= 10.0;
+ }
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ PoissonDistributionImpl dist;
+
+ dist = new PoissonDistributionImpl(1);
+ assertEquals(dist.getNumericalVariance(), 1, tol);
+
+ dist.setMean(11.23);
+ assertEquals(dist.getNumericalVariance(), 11.23, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/TDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/TDistributionTest.java
new file mode 100644
index 0000000..9faf9a7
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/TDistributionTest.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.distribution;
+
+/**
+ * Test cases for TDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class TDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for TDistributionTest.
+ * @param name
+ */
+ public TDistributionTest(String name) {
+ super(name);
+ }
+
+//-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public TDistribution makeDistribution() {
+ return new TDistributionImpl(5.0);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {-5.89342953136, -3.36492999891, -2.57058183564, -2.01504837333, -1.47588404882,
+ 5.89342953136, 3.36492999891, 2.57058183564, 2.01504837333, 1.47588404882};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999,
+ 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.000756494565517, 0.0109109752919, 0.0303377878006, 0.0637967988952, 0.128289492005,
+ 0.000756494565517, 0.0109109752919, 0.0303377878006, 0.0637967988952, 0.128289492005};
+ }
+
+ // --------------------- Override tolerance --------------
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setTolerance(1E-9);
+ }
+
+ //---------------------------- Additional test cases -------------------------
+ /**
+ * @see <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=27243">
+ * Bug report that prompted this unit test.</a>
+ */
+ public void testCumulativeProbabilityAgaintStackOverflow() throws Exception {
+ TDistributionImpl td = new TDistributionImpl(5.);
+ td.cumulativeProbability(.1);
+ td.cumulativeProbability(.01);
+ }
+
+ public void testSmallDf() throws Exception {
+ setDistribution(new TDistributionImpl(1d));
+ // quantiles computed using R version 2.9.2
+ setCumulativeTestPoints(new double[] {-318.308838986, -31.8205159538, -12.7062047362,
+ -6.31375151468, -3.07768353718, 318.308838986, 31.8205159538, 12.7062047362,
+ 6.31375151468, 3.07768353718});
+ setDensityTestValues(new double[] {3.14158231817e-06, 0.000314055924703, 0.00195946145194,
+ 0.00778959736375, 0.0303958893917, 3.14158231817e-06, 0.000314055924703,
+ 0.00195946145194, 0.00778959736375, 0.0303958893917});
+ setInverseCumulativeTestValues(getCumulativeTestPoints());
+ verifyCumulativeProbabilities();
+ verifyInverseCumulativeProbabilities();
+ verifyDensities();
+ }
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0, 1});
+ setInverseCumulativeTestValues(
+ new double[] {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testDfAccessors() {
+ TDistribution distribution = (TDistribution) getDistribution();
+ assertEquals(5d, distribution.getDegreesOfFreedom(), Double.MIN_VALUE);
+ distribution.setDegreesOfFreedom(4d);
+ assertEquals(4d, distribution.getDegreesOfFreedom(), Double.MIN_VALUE);
+ try {
+ distribution.setDegreesOfFreedom(0d);
+ fail("Expecting IllegalArgumentException for df = 0");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ TDistributionImpl dist;
+
+ dist = new TDistributionImpl(1);
+ assertTrue(Double.isNaN(dist.getNumericalMean()));
+ assertTrue(Double.isNaN(dist.getNumericalVariance()));
+
+ dist.setDegreesOfFreedom(1.5);
+ assertEquals(dist.getNumericalMean(), 0, tol);
+ assertTrue(Double.isInfinite(dist.getNumericalVariance()));
+
+ dist.setDegreesOfFreedom(5);
+ assertEquals(dist.getNumericalMean(), 0, tol);
+ assertEquals(dist.getNumericalVariance(), 5d / (5d - 2d), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/WeibullDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/WeibullDistributionTest.java
new file mode 100644
index 0000000..a25be43
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/WeibullDistributionTest.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.distribution;
+
+import org.apache.commons.math.special.Gamma;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for WeibullDistribution.
+ * Extends ContinuousDistributionAbstractTest. See class javadoc for
+ * ContinuousDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class WeibullDistributionTest extends ContinuousDistributionAbstractTest {
+
+ /**
+ * Constructor for CauchyDistributionTest.
+ * @param arg0
+ */
+ public WeibullDistributionTest(String arg0) {
+ super(arg0);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default continuous distribution instance to use in tests. */
+ @Override
+ public WeibullDistribution makeDistribution() {
+ return new WeibullDistributionImpl(1.2, 2.1);
+ }
+
+ /** Creates the default cumulative probability distribution test input values */
+ @Override
+ public double[] makeCumulativeTestPoints() {
+ // quantiles computed using R version 2.9.2
+ return new double[] {0.00664355180993, 0.0454328283309, 0.0981162737374, 0.176713524579, 0.321946865392,
+ 10.5115496887, 7.4976304671, 6.23205600701, 5.23968436955, 4.2079028257};
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0.001, 0.01, 0.025, 0.05, 0.1, 0.999, 0.990, 0.975, 0.950, 0.900};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0.180535929306, 0.262801138133, 0.301905425199, 0.330899152971,
+ 0.353441418887, 0.000788590320203, 0.00737060094841, 0.0177576041516, 0.0343043442574, 0.065664589369};
+ }
+
+ //---------------------------- Additional test cases -------------------------
+
+ public void testInverseCumulativeProbabilityExtremes() throws Exception {
+ setInverseCumulativeTestPoints(new double[] {0.0, 1.0});
+ setInverseCumulativeTestValues(
+ new double[] {0.0, Double.POSITIVE_INFINITY});
+ verifyInverseCumulativeProbabilities();
+ }
+
+ public void testAlpha() {
+ WeibullDistribution distribution = (WeibullDistribution) getDistribution();
+ double expected = FastMath.random();
+ distribution.setShape(expected);
+ assertEquals(expected, distribution.getShape(), 0.0);
+ }
+
+ public void testBeta() {
+ WeibullDistribution distribution = (WeibullDistribution) getDistribution();
+ double expected = FastMath.random();
+ distribution.setScale(expected);
+ assertEquals(expected, distribution.getScale(), 0.0);
+ }
+
+ public void testSetAlpha() {
+ WeibullDistribution distribution = (WeibullDistribution) getDistribution();
+ try {
+ distribution.setShape(0.0);
+ fail("Can not have 0.0 alpha.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ distribution.setShape(-1.0);
+ fail("Can not have negative alpha.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testSetBeta() {
+ WeibullDistribution distribution = (WeibullDistribution) getDistribution();
+ try {
+ distribution.setScale(0.0);
+ fail("Can not have 0.0 beta.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ distribution.setScale(-1.0);
+ fail("Can not have negative beta.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ WeibullDistributionImpl dist;
+
+ dist = new WeibullDistributionImpl(2.5, 3.5);
+ // In R: 3.5*gamma(1+(1/2.5)) (or emperically: mean(rweibull(10000, 2.5, 3.5)))
+ assertEquals(dist.getNumericalMean(), 3.5 * FastMath.exp(Gamma.logGamma(1 + (1 / 2.5))), tol);
+ assertEquals(dist.getNumericalVariance(), (3.5 * 3.5) *
+ FastMath.exp(Gamma.logGamma(1 + (2 / 2.5))) -
+ (dist.getNumericalMean() * dist.getNumericalMean()), tol);
+
+ dist.setShape(10.4);
+ dist.setScale(2.222);
+ assertEquals(dist.getNumericalMean(), 2.222 * FastMath.exp(Gamma.logGamma(1 + (1 / 10.4))), tol);
+ assertEquals(dist.getNumericalVariance(), (2.222 * 2.222) *
+ FastMath.exp(Gamma.logGamma(1 + (2 / 10.4))) -
+ (dist.getNumericalMean() * dist.getNumericalMean()), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/distribution/ZipfDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/ZipfDistributionTest.java
new file mode 100644
index 0000000..5a8c173
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/distribution/ZipfDistributionTest.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.distribution;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for {@link ZipfDistribution}.
+ * Extends IntegerDistributionAbstractTest. See class javadoc for
+ * IntegerDistributionAbstractTest for details.
+ *
+ * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
+ */
+public class ZipfDistributionTest extends IntegerDistributionAbstractTest {
+ public ZipfDistributionTest(String name) {
+ super(name);
+ }
+
+ //-------------- Implementations for abstract methods -----------------------
+
+ /** Creates the default discrete distribution instance to use in tests. */
+ @Override
+ public IntegerDistribution makeDistribution() {
+ return new ZipfDistributionImpl(10, 1);
+ }
+
+ /** Creates the default probability density test input values */
+ @Override
+ public int[] makeDensityTestPoints() {
+ return new int[] {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+ }
+
+ /** Creates the default probability density test expected values */
+ @Override
+ public double[] makeDensityTestValues() {
+ return new double[] {0d, 0d, 0.3414d, 0.1707d, 0.1138d, 0.0854d, 0.0683d,
+ 0.0569d, 0.0488d, 0.0427d, 0.0379d, 0.0341d, 0d};
+ }
+
+ /** Creates the default cumulative probability density test input values */
+ @Override
+ public int[] makeCumulativeTestPoints() {
+ return makeDensityTestPoints();
+ }
+
+ /** Creates the default cumulative probability density test expected values */
+ @Override
+ public double[] makeCumulativeTestValues() {
+ return new double[] {0d, 0.0000d, 0.3414d, 0.5121d, 0.6259d, 0.7113d,
+ 0.7796d, 0.8365d, 0.8852d, 0.9279d, 0.9659d, 1d, 1d};
+ }
+
+ /** Creates the default inverse cumulative probability test input values */
+ @Override
+ public double[] makeInverseCumulativeTestPoints() {
+ return new double[] {0, 0.001d, 0.010d, 0.025d, 0.050d, 0.3414d, 0.3415d, 0.999d,
+ 0.990d, 0.975d, 0.950d, 0.900d, 1};
+ }
+
+ /** Creates the default inverse cumulative probability density test expected values */
+ @Override
+ public int[] makeInverseCumulativeTestValues() {
+ return new int[] {0, 0, 0, 0, 0, 0, 1, 9, 9, 9, 8, 7, 10};
+ }
+
+ public void testMomonts() {
+ final double tol = 1e-9;
+ ZipfDistributionImpl dist;
+
+ dist = new ZipfDistributionImpl(2, 0.5);
+ assertEquals(dist.getNumericalMean(), FastMath.sqrt(2), tol);
+ assertEquals(dist.getNumericalVariance(), 0.24264068711928521, tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/estimation/EstimatedParameterTest.java b/src/test/java/org/apache/commons/math/estimation/EstimatedParameterTest.java
new file mode 100644
index 0000000..553cebf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/estimation/EstimatedParameterTest.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.estimation;
+
+import org.apache.commons.math.estimation.EstimatedParameter;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+ at Deprecated
+public class EstimatedParameterTest
+ extends TestCase {
+
+ public EstimatedParameterTest(String name) {
+ super(name);
+ }
+
+ public void testConstruction() {
+
+ EstimatedParameter p1 = new EstimatedParameter("p1", 1.0);
+ assertTrue(p1.getName().equals("p1"));
+ checkValue(p1.getEstimate(), 1.0);
+ assertTrue(! p1.isBound());
+
+ EstimatedParameter p2 = new EstimatedParameter("p2", 2.0, true);
+ assertTrue(p2.getName().equals("p2"));
+ checkValue(p2.getEstimate(), 2.0);
+ assertTrue(p2.isBound());
+
+ }
+
+ public void testBound() {
+
+ EstimatedParameter p = new EstimatedParameter("p", 0.0);
+ assertTrue(! p.isBound());
+ p.setBound(true);
+ assertTrue(p.isBound());
+ p.setBound(false);
+ assertTrue(! p.isBound());
+
+ }
+
+ public void testEstimate() {
+
+ EstimatedParameter p = new EstimatedParameter("p", 0.0);
+ checkValue(p.getEstimate(), 0.0);
+
+ for (double e = 0.0; e < 10.0; e += 0.5) {
+ p.setEstimate(e);
+ checkValue(p.getEstimate(), e);
+ }
+
+ }
+
+ private void checkValue(double value, double expected) {
+ assertTrue(FastMath.abs(value - expected) < 1.0e-10);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/estimation/GaussNewtonEstimatorTest.java b/src/test/java/org/apache/commons/math/estimation/GaussNewtonEstimatorTest.java
new file mode 100644
index 0000000..a9d9fc1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/estimation/GaussNewtonEstimatorTest.java
@@ -0,0 +1,724 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.HashSet;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+ at Deprecated
+public class GaussNewtonEstimatorTest
+ extends TestCase {
+
+ public GaussNewtonEstimatorTest(String name) {
+ super(name);
+ }
+
+ public void testTrivial() throws EstimationException {
+ LinearProblem problem =
+ new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] {2},
+ new EstimatedParameter[] {
+ new EstimatedParameter("p0", 0)
+ }, 3.0)
+ });
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(1.5,
+ problem.getUnboundParameters()[0].getEstimate(),
+ 1.0e-10);
+ }
+
+ public void testQRColumnsPermutation() throws EstimationException {
+
+ EstimatedParameter[] x = {
+ new EstimatedParameter("p0", 0), new EstimatedParameter("p1", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { x[0], x[1] },
+ 4.0),
+ new LinearMeasurement(new double[] { 2.0 },
+ new EstimatedParameter[] { x[1] },
+ 6.0),
+ new LinearMeasurement(new double[] { 1.0, -2.0 },
+ new EstimatedParameter[] { x[0], x[1] },
+ 1.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(7.0, x[0].getEstimate(), 1.0e-10);
+ assertEquals(3.0, x[1].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testNoDependency() throws EstimationException {
+ EstimatedParameter[] p = new EstimatedParameter[] {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0),
+ new EstimatedParameter("p3", 0),
+ new EstimatedParameter("p4", 0),
+ new EstimatedParameter("p5", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[0] }, 0.0),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[1] }, 1.1),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[2] }, 2.2),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[3] }, 3.3),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[4] }, 4.4),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[5] }, 5.5)
+ });
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ for (int i = 0; i < p.length; ++i) {
+ assertEquals(0.55 * i, p[i].getEstimate(), 1.0e-10);
+ }
+}
+
+ public void testOneSet() throws EstimationException {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0 },
+ new EstimatedParameter[] { p[0] },
+ 1.0),
+ new LinearMeasurement(new double[] { -1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { -1.0, 1.0 },
+ new EstimatedParameter[] { p[1], p[2] },
+ 1.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(1.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals(2.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(3.0, p[2].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testTwoSets() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 1),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 3),
+ new EstimatedParameter("p4", 4),
+ new EstimatedParameter("p5", 5)
+ };
+
+ double epsilon = 1.0e-7;
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+
+ // 4 elements sub-problem
+ new LinearMeasurement(new double[] { 2.0, 1.0, 4.0 },
+ new EstimatedParameter[] { p[0], p[1], p[3] },
+ 2.0),
+ new LinearMeasurement(new double[] { -4.0, -2.0, 3.0, -7.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ -9.0),
+ new LinearMeasurement(new double[] { 4.0, 1.0, -2.0, 8.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 2.0),
+ new LinearMeasurement(new double[] { -3.0, -12.0, -1.0 },
+ new EstimatedParameter[] { p[1], p[2], p[3] },
+ 2.0),
+
+ // 2 elements sub-problem
+ new LinearMeasurement(new double[] { epsilon, 1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ 1.0 + epsilon * epsilon),
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ 2.0)
+
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals( 3.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals( 4.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(-1.0, p[2].getEstimate(), 1.0e-10);
+ assertEquals(-2.0, p[3].getEstimate(), 1.0e-10);
+ assertEquals( 1.0 + epsilon, p[4].getEstimate(), 1.0e-10);
+ assertEquals( 1.0 - epsilon, p[5].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testNonInversible() {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0)
+ };
+ LinearMeasurement[] m = new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 2.0, -3.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 1.0),
+ new LinearMeasurement(new double[] { 2.0, 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 1.0),
+ new LinearMeasurement(new double[] { -3.0, -9.0 },
+ new EstimatedParameter[] { p[0], p[2] },
+ 1.0)
+ };
+ LinearProblem problem = new LinearProblem(m);
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ try {
+ estimator.estimate(problem);
+ fail("an exception should have been caught");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+ }
+
+ public void testIllConditioned() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 1),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 3)
+ };
+
+ LinearProblem problem1 = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 10.0, 7.0, 8.0, 7.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 32.0),
+ new LinearMeasurement(new double[] { 7.0, 5.0, 6.0, 5.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 23.0),
+ new LinearMeasurement(new double[] { 8.0, 6.0, 10.0, 9.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 33.0),
+ new LinearMeasurement(new double[] { 7.0, 5.0, 9.0, 10.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 31.0)
+ });
+ GaussNewtonEstimator estimator1 = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator1.estimate(problem1);
+ assertEquals(0, estimator1.getRMS(problem1), 1.0e-10);
+ assertEquals(1.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[2].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[3].getEstimate(), 1.0e-10);
+
+ LinearProblem problem2 = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 10.0, 7.0, 8.1, 7.2 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 32.0),
+ new LinearMeasurement(new double[] { 7.08, 5.04, 6.0, 5.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 23.0),
+ new LinearMeasurement(new double[] { 8.0, 5.98, 9.89, 9.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 33.0),
+ new LinearMeasurement(new double[] { 6.99, 4.99, 9.0, 9.98 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 31.0)
+ });
+ GaussNewtonEstimator estimator2 = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator2.estimate(problem2);
+ assertEquals(0, estimator2.getRMS(problem2), 1.0e-10);
+ assertEquals(-81.0, p[0].getEstimate(), 1.0e-8);
+ assertEquals(137.0, p[1].getEstimate(), 1.0e-8);
+ assertEquals(-34.0, p[2].getEstimate(), 1.0e-8);
+ assertEquals( 22.0, p[3].getEstimate(), 1.0e-8);
+
+ }
+
+ public void testMoreEstimatedParametersSimple() {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 7),
+ new EstimatedParameter("p1", 6),
+ new EstimatedParameter("p2", 5),
+ new EstimatedParameter("p3", 4)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 3.0, 2.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 7.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0, 1.0 },
+ new EstimatedParameter[] { p[1], p[2], p[3] },
+ 3.0),
+ new LinearMeasurement(new double[] { 2.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[2] },
+ 5.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ try {
+ estimator.estimate(problem);
+ fail("an exception should have been caught");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ public void testMoreEstimatedParametersUnsorted() {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 2),
+ new EstimatedParameter("p1", 2),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 2),
+ new EstimatedParameter("p4", 2),
+ new EstimatedParameter("p5", 2)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, 1.0, 1.0 },
+ new EstimatedParameter[] { p[2], p[3], p[4] },
+ 12.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ -1.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0, 1.0 },
+ new EstimatedParameter[] { p[3], p[2], p[5] },
+ 7.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[4], p[3] },
+ 1.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ try {
+ estimator.estimate(problem);
+ fail("an exception should have been caught");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ public void testRedundantEquations() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 1),
+ new EstimatedParameter("p1", 1)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 5.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ EstimatedParameter[] all = problem.getAllParameters();
+ for (int i = 0; i < all.length; ++i) {
+ assertEquals(all[i].getName().equals("p0") ? 2.0 : 1.0,
+ all[i].getEstimate(), 1.0e-10);
+ }
+
+ }
+
+ public void testInconsistentEquations() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 1),
+ new EstimatedParameter("p1", 1)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 4.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertTrue(estimator.getRMS(problem) > 0.1);
+
+ }
+
+ public void testBoundParameters() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("unbound0", 2, false),
+ new EstimatedParameter("unbound1", 2, false),
+ new EstimatedParameter("bound", 2, true)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 1.0),
+ new LinearMeasurement(new double[] { 1.0, 3.0, 2.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 7.0)
+ });
+
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ estimator.estimate(problem);
+ assertTrue(estimator.getRMS(problem) < 1.0e-10);
+ double[][] covariances = estimator.getCovariances(problem);
+ int i0 = 0, i1 = 1;
+ if (problem.getUnboundParameters()[0].getName().endsWith("1")) {
+ i0 = 1;
+ i1 = 0;
+ }
+ assertEquals(11.0 / 24, covariances[i0][i0], 1.0e-10);
+ assertEquals(-3.0 / 24, covariances[i0][i1], 1.0e-10);
+ assertEquals(-3.0 / 24, covariances[i1][i0], 1.0e-10);
+ assertEquals( 3.0 / 24, covariances[i1][i1], 1.0e-10);
+
+ double[] errors = estimator.guessParametersErrors(problem);
+ assertEquals(0, errors[i0], 1.0e-10);
+ assertEquals(0, errors[i1], 1.0e-10);
+
+ }
+
+ public void testMaxIterations() {
+ Circle circle = new Circle(98.680, 47.345);
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ try {
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(4, 1.0e-14, 1.0e-14);
+ estimator.estimate(circle);
+ fail("an exception should have been caught");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+ }
+
+ public void testCircleFitting() throws EstimationException {
+ Circle circle = new Circle(98.680, 47.345);
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-10, 1.0e-10);
+ estimator.estimate(circle);
+ double rms = estimator.getRMS(circle);
+ assertEquals(1.768262623567235, FastMath.sqrt(circle.getM()) * rms, 1.0e-10);
+ assertEquals(69.96016176931406, circle.getRadius(), 1.0e-10);
+ assertEquals(96.07590211815305, circle.getX(), 1.0e-10);
+ assertEquals(48.13516790438953, circle.getY(), 1.0e-10);
+ }
+
+ public void testCircleFittingBadInit() {
+ Circle circle = new Circle(-12, -12);
+ double[][] points = new double[][] {
+ {-0.312967, 0.072366}, {-0.339248, 0.132965}, {-0.379780, 0.202724},
+ {-0.390426, 0.260487}, {-0.361212, 0.328325}, {-0.346039, 0.392619},
+ {-0.280579, 0.444306}, {-0.216035, 0.470009}, {-0.149127, 0.493832},
+ {-0.075133, 0.483271}, {-0.007759, 0.452680}, { 0.060071, 0.410235},
+ { 0.103037, 0.341076}, { 0.118438, 0.273884}, { 0.131293, 0.192201},
+ { 0.115869, 0.129797}, { 0.072223, 0.058396}, { 0.022884, 0.000718},
+ {-0.053355, -0.020405}, {-0.123584, -0.032451}, {-0.216248, -0.032862},
+ {-0.278592, -0.005008}, {-0.337655, 0.056658}, {-0.385899, 0.112526},
+ {-0.405517, 0.186957}, {-0.415374, 0.262071}, {-0.387482, 0.343398},
+ {-0.347322, 0.397943}, {-0.287623, 0.458425}, {-0.223502, 0.475513},
+ {-0.135352, 0.478186}, {-0.061221, 0.483371}, { 0.003711, 0.422737},
+ { 0.065054, 0.375830}, { 0.108108, 0.297099}, { 0.123882, 0.222850},
+ { 0.117729, 0.134382}, { 0.085195, 0.056820}, { 0.029800, -0.019138},
+ {-0.027520, -0.072374}, {-0.102268, -0.091555}, {-0.200299, -0.106578},
+ {-0.292731, -0.091473}, {-0.356288, -0.051108}, {-0.420561, 0.014926},
+ {-0.471036, 0.074716}, {-0.488638, 0.182508}, {-0.485990, 0.254068},
+ {-0.463943, 0.338438}, {-0.406453, 0.404704}, {-0.334287, 0.466119},
+ {-0.254244, 0.503188}, {-0.161548, 0.495769}, {-0.075733, 0.495560},
+ { 0.001375, 0.434937}, { 0.082787, 0.385806}, { 0.115490, 0.323807},
+ { 0.141089, 0.223450}, { 0.138693, 0.131703}, { 0.126415, 0.049174},
+ { 0.066518, -0.010217}, {-0.005184, -0.070647}, {-0.080985, -0.103635},
+ {-0.177377, -0.116887}, {-0.260628, -0.100258}, {-0.335756, -0.056251},
+ {-0.405195, -0.000895}, {-0.444937, 0.085456}, {-0.484357, 0.175597},
+ {-0.472453, 0.248681}, {-0.438580, 0.347463}, {-0.402304, 0.422428},
+ {-0.326777, 0.479438}, {-0.247797, 0.505581}, {-0.152676, 0.519380},
+ {-0.071754, 0.516264}, { 0.015942, 0.472802}, { 0.076608, 0.419077},
+ { 0.127673, 0.330264}, { 0.159951, 0.262150}, { 0.153530, 0.172681},
+ { 0.140653, 0.089229}, { 0.078666, 0.024981}, { 0.023807, -0.037022},
+ {-0.048837, -0.077056}, {-0.127729, -0.075338}, {-0.221271, -0.067526}
+ };
+ for (int i = 0; i < points.length; ++i) {
+ circle.addPoint(points[i][0], points[i][1]);
+ }
+ GaussNewtonEstimator estimator = new GaussNewtonEstimator(100, 1.0e-6, 1.0e-6);
+ try {
+ estimator.estimate(circle);
+ fail("an exception should have been caught");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+}
+
+ private static class LinearProblem extends SimpleEstimationProblem {
+
+ public LinearProblem(LinearMeasurement[] measurements) {
+ HashSet<EstimatedParameter> set = new HashSet<EstimatedParameter>();
+ for (int i = 0; i < measurements.length; ++i) {
+ addMeasurement(measurements[i]);
+ EstimatedParameter[] parameters = measurements[i].getParameters();
+ for (int j = 0; j < parameters.length; ++j) {
+ set.add(parameters[j]);
+ }
+ }
+ for (EstimatedParameter p : set) {
+ addParameter(p);
+ }
+ }
+
+ }
+
+ private static class LinearMeasurement extends WeightedMeasurement {
+
+ public LinearMeasurement(double[] factors, EstimatedParameter[] parameters,
+ double setPoint) {
+ super(1.0, setPoint, true);
+ this.factors = factors;
+ this.parameters = parameters;
+ setIgnored(false);
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ double v = 0;
+ for (int i = 0; i < factors.length; ++i) {
+ v += factors[i] * parameters[i].getEstimate();
+ }
+ return v;
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ for (int i = 0; i < parameters.length; ++i) {
+ if (parameters[i] == parameter) {
+ return factors[i];
+ }
+ }
+ return 0;
+ }
+
+ public EstimatedParameter[] getParameters() {
+ return parameters;
+ }
+
+ private double[] factors;
+ private EstimatedParameter[] parameters;
+ private static final long serialVersionUID = -3922448707008868580L;
+
+ }
+
+ private static class Circle implements EstimationProblem {
+
+ public Circle(double cx, double cy) {
+ this.cx = new EstimatedParameter("cx", cx);
+ this.cy = new EstimatedParameter(new EstimatedParameter("cy", cy));
+ points = new ArrayList<PointModel>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new PointModel(this, px, py));
+ }
+
+ public int getM() {
+ return points.size();
+ }
+
+ public WeightedMeasurement[] getMeasurements() {
+ return points.toArray(new PointModel[points.size()]);
+ }
+
+ public EstimatedParameter[] getAllParameters() {
+ return new EstimatedParameter[] { cx, cy };
+ }
+
+ public EstimatedParameter[] getUnboundParameters() {
+ return new EstimatedParameter[] { cx, cy };
+ }
+
+ public double getPartialRadiusX() {
+ double dRdX = 0;
+ for (PointModel point : points) {
+ dRdX += point.getPartialDiX();
+ }
+ return dRdX / points.size();
+ }
+
+ public double getPartialRadiusY() {
+ double dRdY = 0;
+ for (PointModel point : points) {
+ dRdY += point.getPartialDiY();
+ }
+ return dRdY / points.size();
+ }
+
+ public double getRadius() {
+ double r = 0;
+ for (PointModel point : points) {
+ r += point.getCenterDistance();
+ }
+ return r / points.size();
+ }
+
+ public double getX() {
+ return cx.getEstimate();
+ }
+
+ public double getY() {
+ return cy.getEstimate();
+ }
+
+ private static class PointModel extends WeightedMeasurement {
+
+ public PointModel(Circle circle, double px, double py) {
+ super(1.0, 0.0);
+ this.px = px;
+ this.py = py;
+ this.circle = circle;
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ if (parameter == circle.cx) {
+ return getPartialDiX() - circle.getPartialRadiusX();
+ } else if (parameter == circle.cy) {
+ return getPartialDiY() - circle.getPartialRadiusY();
+ }
+ return 0;
+ }
+
+ public double getCenterDistance() {
+ double dx = px - circle.cx.getEstimate();
+ double dy = py - circle.cy.getEstimate();
+ return FastMath.sqrt(dx * dx + dy * dy);
+ }
+
+ public double getPartialDiX() {
+ return (circle.cx.getEstimate() - px) / getCenterDistance();
+ }
+
+ public double getPartialDiY() {
+ return (circle.cy.getEstimate() - py) / getCenterDistance();
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ return getCenterDistance() - circle.getRadius();
+ }
+
+ private double px;
+ private double py;
+ private transient final Circle circle;
+ private static final long serialVersionUID = 1L;
+
+ }
+
+ private EstimatedParameter cx;
+ private EstimatedParameter cy;
+ private ArrayList<PointModel> points;
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimatorTest.java b/src/test/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimatorTest.java
new file mode 100644
index 0000000..676e2cd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/estimation/LevenbergMarquardtEstimatorTest.java
@@ -0,0 +1,832 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.HashSet;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+ at Deprecated
+public class LevenbergMarquardtEstimatorTest
+ extends TestCase {
+
+ public LevenbergMarquardtEstimatorTest(String name) {
+ super(name);
+ }
+
+ public void testTrivial() throws EstimationException {
+ LinearProblem problem =
+ new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] {2},
+ new EstimatedParameter[] {
+ new EstimatedParameter("p0", 0)
+ }, 3.0)
+ });
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ try {
+ estimator.guessParametersErrors(problem);
+ fail("an exception should have been thrown");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+ assertEquals(1.5,
+ problem.getUnboundParameters()[0].getEstimate(),
+ 1.0e-10);
+ }
+
+ public void testQRColumnsPermutation() throws EstimationException {
+
+ EstimatedParameter[] x = {
+ new EstimatedParameter("p0", 0), new EstimatedParameter("p1", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { x[0], x[1] },
+ 4.0),
+ new LinearMeasurement(new double[] { 2.0 },
+ new EstimatedParameter[] { x[1] },
+ 6.0),
+ new LinearMeasurement(new double[] { 1.0, -2.0 },
+ new EstimatedParameter[] { x[0], x[1] },
+ 1.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(7.0, x[0].getEstimate(), 1.0e-10);
+ assertEquals(3.0, x[1].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testNoDependency() throws EstimationException {
+ EstimatedParameter[] p = new EstimatedParameter[] {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0),
+ new EstimatedParameter("p3", 0),
+ new EstimatedParameter("p4", 0),
+ new EstimatedParameter("p5", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[0] }, 0.0),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[1] }, 1.1),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[2] }, 2.2),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[3] }, 3.3),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[4] }, 4.4),
+ new LinearMeasurement(new double[] {2}, new EstimatedParameter[] { p[5] }, 5.5)
+ });
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ for (int i = 0; i < p.length; ++i) {
+ assertEquals(0.55 * i, p[i].getEstimate(), 1.0e-10);
+ }
+}
+
+ public void testOneSet() throws EstimationException {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0 },
+ new EstimatedParameter[] { p[0] },
+ 1.0),
+ new LinearMeasurement(new double[] { -1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { -1.0, 1.0 },
+ new EstimatedParameter[] { p[1], p[2] },
+ 1.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(1.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals(2.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(3.0, p[2].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testTwoSets() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 1),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 3),
+ new EstimatedParameter("p4", 4),
+ new EstimatedParameter("p5", 5)
+ };
+
+ double epsilon = 1.0e-7;
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+
+ // 4 elements sub-problem
+ new LinearMeasurement(new double[] { 2.0, 1.0, 4.0 },
+ new EstimatedParameter[] { p[0], p[1], p[3] },
+ 2.0),
+ new LinearMeasurement(new double[] { -4.0, -2.0, 3.0, -7.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ -9.0),
+ new LinearMeasurement(new double[] { 4.0, 1.0, -2.0, 8.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 2.0),
+ new LinearMeasurement(new double[] { -3.0, -12.0, -1.0 },
+ new EstimatedParameter[] { p[1], p[2], p[3] },
+ 2.0),
+
+ // 2 elements sub-problem
+ new LinearMeasurement(new double[] { epsilon, 1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ 1.0 + epsilon * epsilon),
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ 2.0)
+
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals( 3.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals( 4.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(-1.0, p[2].getEstimate(), 1.0e-10);
+ assertEquals(-2.0, p[3].getEstimate(), 1.0e-10);
+ assertEquals( 1.0 + epsilon, p[4].getEstimate(), 1.0e-10);
+ assertEquals( 1.0 - epsilon, p[5].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testNonInversible() throws EstimationException {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 0),
+ new EstimatedParameter("p2", 0)
+ };
+ LinearMeasurement[] m = new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 2.0, -3.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 1.0),
+ new LinearMeasurement(new double[] { 2.0, 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2] },
+ 1.0),
+ new LinearMeasurement(new double[] { -3.0, -9.0 },
+ new EstimatedParameter[] { p[0], p[2] },
+ 1.0)
+ };
+ LinearProblem problem = new LinearProblem(m);
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ double initialCost = estimator.getRMS(problem);
+ estimator.estimate(problem);
+ assertTrue(estimator.getRMS(problem) < initialCost);
+ assertTrue(FastMath.sqrt(m.length) * estimator.getRMS(problem) > 0.6);
+ try {
+ estimator.getCovariances(problem);
+ fail("an exception should have been thrown");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+ double dJ0 = 2 * (m[0].getResidual() * m[0].getPartial(p[0])
+ + m[1].getResidual() * m[1].getPartial(p[0])
+ + m[2].getResidual() * m[2].getPartial(p[0]));
+ double dJ1 = 2 * (m[0].getResidual() * m[0].getPartial(p[1])
+ + m[1].getResidual() * m[1].getPartial(p[1]));
+ double dJ2 = 2 * (m[0].getResidual() * m[0].getPartial(p[2])
+ + m[1].getResidual() * m[1].getPartial(p[2])
+ + m[2].getResidual() * m[2].getPartial(p[2]));
+ assertEquals(0, dJ0, 1.0e-10);
+ assertEquals(0, dJ1, 1.0e-10);
+ assertEquals(0, dJ2, 1.0e-10);
+
+ }
+
+ public void testIllConditioned() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 0),
+ new EstimatedParameter("p1", 1),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 3)
+ };
+
+ LinearProblem problem1 = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 10.0, 7.0, 8.0, 7.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 32.0),
+ new LinearMeasurement(new double[] { 7.0, 5.0, 6.0, 5.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 23.0),
+ new LinearMeasurement(new double[] { 8.0, 6.0, 10.0, 9.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 33.0),
+ new LinearMeasurement(new double[] { 7.0, 5.0, 9.0, 10.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 31.0)
+ });
+ LevenbergMarquardtEstimator estimator1 = new LevenbergMarquardtEstimator();
+ estimator1.estimate(problem1);
+ assertEquals(0, estimator1.getRMS(problem1), 1.0e-10);
+ assertEquals(1.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[1].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[2].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[3].getEstimate(), 1.0e-10);
+
+ LinearProblem problem2 = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 10.0, 7.0, 8.1, 7.2 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 32.0),
+ new LinearMeasurement(new double[] { 7.08, 5.04, 6.0, 5.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 23.0),
+ new LinearMeasurement(new double[] { 8.0, 5.98, 9.89, 9.0 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 33.0),
+ new LinearMeasurement(new double[] { 6.99, 4.99, 9.0, 9.98 },
+ new EstimatedParameter[] { p[0], p[1], p[2], p[3] },
+ 31.0)
+ });
+ LevenbergMarquardtEstimator estimator2 = new LevenbergMarquardtEstimator();
+ estimator2.estimate(problem2);
+ assertEquals(0, estimator2.getRMS(problem2), 1.0e-10);
+ assertEquals(-81.0, p[0].getEstimate(), 1.0e-8);
+ assertEquals(137.0, p[1].getEstimate(), 1.0e-8);
+ assertEquals(-34.0, p[2].getEstimate(), 1.0e-8);
+ assertEquals( 22.0, p[3].getEstimate(), 1.0e-8);
+
+ }
+
+ public void testMoreEstimatedParametersSimple() throws EstimationException {
+
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 7),
+ new EstimatedParameter("p1", 6),
+ new EstimatedParameter("p2", 5),
+ new EstimatedParameter("p3", 4)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 3.0, 2.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 7.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0, 1.0 },
+ new EstimatedParameter[] { p[1], p[2], p[3] },
+ 3.0),
+ new LinearMeasurement(new double[] { 2.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[2] },
+ 5.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+
+ }
+
+ public void testMoreEstimatedParametersUnsorted() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 2),
+ new EstimatedParameter("p1", 2),
+ new EstimatedParameter("p2", 2),
+ new EstimatedParameter("p3", 2),
+ new EstimatedParameter("p4", 2),
+ new EstimatedParameter("p5", 2)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, 1.0, 1.0 },
+ new EstimatedParameter[] { p[2], p[3], p[4] },
+ 12.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[4], p[5] },
+ -1.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0, 1.0 },
+ new EstimatedParameter[] { p[3], p[2], p[5] },
+ 7.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[4], p[3] },
+ 1.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(3.0, p[2].getEstimate(), 1.0e-10);
+ assertEquals(4.0, p[3].getEstimate(), 1.0e-10);
+ assertEquals(5.0, p[4].getEstimate(), 1.0e-10);
+ assertEquals(6.0, p[5].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testRedundantEquations() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 1),
+ new EstimatedParameter("p1", 1)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 5.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertEquals(0, estimator.getRMS(problem), 1.0e-10);
+ assertEquals(2.0, p[0].getEstimate(), 1.0e-10);
+ assertEquals(1.0, p[1].getEstimate(), 1.0e-10);
+
+ }
+
+ public void testInconsistentEquations() throws EstimationException {
+ EstimatedParameter[] p = {
+ new EstimatedParameter("p0", 1),
+ new EstimatedParameter("p1", 1)
+ };
+ LinearProblem problem = new LinearProblem(new LinearMeasurement[] {
+ new LinearMeasurement(new double[] { 1.0, 1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 3.0),
+ new LinearMeasurement(new double[] { 1.0, -1.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 1.0),
+ new LinearMeasurement(new double[] { 1.0, 3.0 },
+ new EstimatedParameter[] { p[0], p[1] },
+ 4.0)
+ });
+
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(problem);
+ assertTrue(estimator.getRMS(problem) > 0.1);
+
+ }
+
+ public void testControlParameters() {
+ Circle circle = new Circle(98.680, 47.345);
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ checkEstimate(circle, 0.1, 10, 1.0e-14, 1.0e-16, 1.0e-10, false);
+ checkEstimate(circle, 0.1, 10, 1.0e-15, 1.0e-17, 1.0e-10, true);
+ checkEstimate(circle, 0.1, 5, 1.0e-15, 1.0e-16, 1.0e-10, true);
+ circle.addPoint(300, -300);
+ checkEstimate(circle, 0.1, 20, 1.0e-18, 1.0e-16, 1.0e-10, true);
+ }
+
+ private void checkEstimate(EstimationProblem problem,
+ double initialStepBoundFactor, int maxCostEval,
+ double costRelativeTolerance, double parRelativeTolerance,
+ double orthoTolerance, boolean shouldFail) {
+ try {
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.setInitialStepBoundFactor(initialStepBoundFactor);
+ estimator.setMaxCostEval(maxCostEval);
+ estimator.setCostRelativeTolerance(costRelativeTolerance);
+ estimator.setParRelativeTolerance(parRelativeTolerance);
+ estimator.setOrthoTolerance(orthoTolerance);
+ estimator.estimate(problem);
+ assertTrue(! shouldFail);
+ } catch (EstimationException ee) {
+ assertTrue(shouldFail);
+ }
+ }
+
+ public void testCircleFitting() throws EstimationException {
+ Circle circle = new Circle(98.680, 47.345);
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(circle);
+ assertTrue(estimator.getCostEvaluations() < 10);
+ assertTrue(estimator.getJacobianEvaluations() < 10);
+ double rms = estimator.getRMS(circle);
+ assertEquals(1.768262623567235, FastMath.sqrt(circle.getM()) * rms, 1.0e-10);
+ assertEquals(69.96016176931406, circle.getRadius(), 1.0e-10);
+ assertEquals(96.07590211815305, circle.getX(), 1.0e-10);
+ assertEquals(48.13516790438953, circle.getY(), 1.0e-10);
+ double[][] cov = estimator.getCovariances(circle);
+ assertEquals(1.839, cov[0][0], 0.001);
+ assertEquals(0.731, cov[0][1], 0.001);
+ assertEquals(cov[0][1], cov[1][0], 1.0e-14);
+ assertEquals(0.786, cov[1][1], 0.001);
+ double[] errors = estimator.guessParametersErrors(circle);
+ assertEquals(1.384, errors[0], 0.001);
+ assertEquals(0.905, errors[1], 0.001);
+
+ // add perfect measurements and check errors are reduced
+ double cx = circle.getX();
+ double cy = circle.getY();
+ double r = circle.getRadius();
+ for (double d= 0; d < 2 * FastMath.PI; d += 0.01) {
+ circle.addPoint(cx + r * FastMath.cos(d), cy + r * FastMath.sin(d));
+ }
+ estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(circle);
+ cov = estimator.getCovariances(circle);
+ assertEquals(0.004, cov[0][0], 0.001);
+ assertEquals(6.40e-7, cov[0][1], 1.0e-9);
+ assertEquals(cov[0][1], cov[1][0], 1.0e-14);
+ assertEquals(0.003, cov[1][1], 0.001);
+ errors = estimator.guessParametersErrors(circle);
+ assertEquals(0.004, errors[0], 0.001);
+ assertEquals(0.004, errors[1], 0.001);
+
+ }
+
+ public void testCircleFittingBadInit() throws EstimationException {
+ Circle circle = new Circle(-12, -12);
+ double[][] points = new double[][] {
+ {-0.312967, 0.072366}, {-0.339248, 0.132965}, {-0.379780, 0.202724},
+ {-0.390426, 0.260487}, {-0.361212, 0.328325}, {-0.346039, 0.392619},
+ {-0.280579, 0.444306}, {-0.216035, 0.470009}, {-0.149127, 0.493832},
+ {-0.075133, 0.483271}, {-0.007759, 0.452680}, { 0.060071, 0.410235},
+ { 0.103037, 0.341076}, { 0.118438, 0.273884}, { 0.131293, 0.192201},
+ { 0.115869, 0.129797}, { 0.072223, 0.058396}, { 0.022884, 0.000718},
+ {-0.053355, -0.020405}, {-0.123584, -0.032451}, {-0.216248, -0.032862},
+ {-0.278592, -0.005008}, {-0.337655, 0.056658}, {-0.385899, 0.112526},
+ {-0.405517, 0.186957}, {-0.415374, 0.262071}, {-0.387482, 0.343398},
+ {-0.347322, 0.397943}, {-0.287623, 0.458425}, {-0.223502, 0.475513},
+ {-0.135352, 0.478186}, {-0.061221, 0.483371}, { 0.003711, 0.422737},
+ { 0.065054, 0.375830}, { 0.108108, 0.297099}, { 0.123882, 0.222850},
+ { 0.117729, 0.134382}, { 0.085195, 0.056820}, { 0.029800, -0.019138},
+ {-0.027520, -0.072374}, {-0.102268, -0.091555}, {-0.200299, -0.106578},
+ {-0.292731, -0.091473}, {-0.356288, -0.051108}, {-0.420561, 0.014926},
+ {-0.471036, 0.074716}, {-0.488638, 0.182508}, {-0.485990, 0.254068},
+ {-0.463943, 0.338438}, {-0.406453, 0.404704}, {-0.334287, 0.466119},
+ {-0.254244, 0.503188}, {-0.161548, 0.495769}, {-0.075733, 0.495560},
+ { 0.001375, 0.434937}, { 0.082787, 0.385806}, { 0.115490, 0.323807},
+ { 0.141089, 0.223450}, { 0.138693, 0.131703}, { 0.126415, 0.049174},
+ { 0.066518, -0.010217}, {-0.005184, -0.070647}, {-0.080985, -0.103635},
+ {-0.177377, -0.116887}, {-0.260628, -0.100258}, {-0.335756, -0.056251},
+ {-0.405195, -0.000895}, {-0.444937, 0.085456}, {-0.484357, 0.175597},
+ {-0.472453, 0.248681}, {-0.438580, 0.347463}, {-0.402304, 0.422428},
+ {-0.326777, 0.479438}, {-0.247797, 0.505581}, {-0.152676, 0.519380},
+ {-0.071754, 0.516264}, { 0.015942, 0.472802}, { 0.076608, 0.419077},
+ { 0.127673, 0.330264}, { 0.159951, 0.262150}, { 0.153530, 0.172681},
+ { 0.140653, 0.089229}, { 0.078666, 0.024981}, { 0.023807, -0.037022},
+ {-0.048837, -0.077056}, {-0.127729, -0.075338}, {-0.221271, -0.067526}
+ };
+ for (int i = 0; i < points.length; ++i) {
+ circle.addPoint(points[i][0], points[i][1]);
+ }
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.estimate(circle);
+ assertTrue(estimator.getCostEvaluations() < 15);
+ assertTrue(estimator.getJacobianEvaluations() < 10);
+ assertEquals( 0.030184491196225207, estimator.getRMS(circle), 1.0e-9);
+ assertEquals( 0.2922350065939634, circle.getRadius(), 1.0e-9);
+ assertEquals(-0.15173845023862165, circle.getX(), 1.0e-8);
+ assertEquals( 0.20750021499570379, circle.getY(), 1.0e-8);
+ }
+
+ public void testMath199() {
+ try {
+ QuadraticProblem problem = new QuadraticProblem();
+ problem.addPoint (0, -3.182591015485607, 0.0);
+ problem.addPoint (1, -2.5581184967730577, 4.4E-323);
+ problem.addPoint (2, -2.1488478161387325, 1.0);
+ problem.addPoint (3, -1.9122489313410047, 4.4E-323);
+ problem.addPoint (4, 1.7785661310051026, 0.0);
+ new LevenbergMarquardtEstimator().estimate(problem);
+ fail("an exception should have been thrown");
+ } catch (EstimationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ private static class LinearProblem implements EstimationProblem {
+
+ public LinearProblem(LinearMeasurement[] measurements) {
+ this.measurements = measurements;
+ }
+
+ public WeightedMeasurement[] getMeasurements() {
+ return measurements;
+ }
+
+ public EstimatedParameter[] getUnboundParameters() {
+ return getAllParameters();
+ }
+
+ public EstimatedParameter[] getAllParameters() {
+ HashSet<EstimatedParameter> set = new HashSet<EstimatedParameter>();
+ for (int i = 0; i < measurements.length; ++i) {
+ EstimatedParameter[] parameters = measurements[i].getParameters();
+ for (int j = 0; j < parameters.length; ++j) {
+ set.add(parameters[j]);
+ }
+ }
+ return set.toArray(new EstimatedParameter[set.size()]);
+ }
+
+ private LinearMeasurement[] measurements;
+
+ }
+
+ private static class LinearMeasurement extends WeightedMeasurement {
+
+ public LinearMeasurement(double[] factors, EstimatedParameter[] parameters,
+ double setPoint) {
+ super(1.0, setPoint);
+ this.factors = factors;
+ this.parameters = parameters;
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ double v = 0;
+ for (int i = 0; i < factors.length; ++i) {
+ v += factors[i] * parameters[i].getEstimate();
+ }
+ return v;
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ for (int i = 0; i < parameters.length; ++i) {
+ if (parameters[i] == parameter) {
+ return factors[i];
+ }
+ }
+ return 0;
+ }
+
+ public EstimatedParameter[] getParameters() {
+ return parameters;
+ }
+
+ private double[] factors;
+ private EstimatedParameter[] parameters;
+ private static final long serialVersionUID = -3922448707008868580L;
+
+ }
+
+ private static class Circle implements EstimationProblem {
+
+ public Circle(double cx, double cy) {
+ this.cx = new EstimatedParameter("cx", cx);
+ this.cy = new EstimatedParameter("cy", cy);
+ points = new ArrayList<PointModel>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new PointModel(this, px, py));
+ }
+
+ public int getM() {
+ return points.size();
+ }
+
+ public WeightedMeasurement[] getMeasurements() {
+ return points.toArray(new PointModel[points.size()]);
+ }
+
+ public EstimatedParameter[] getAllParameters() {
+ return new EstimatedParameter[] { cx, cy };
+ }
+
+ public EstimatedParameter[] getUnboundParameters() {
+ return new EstimatedParameter[] { cx, cy };
+ }
+
+ public double getPartialRadiusX() {
+ double dRdX = 0;
+ for (PointModel point : points) {
+ dRdX += point.getPartialDiX();
+ }
+ return dRdX / points.size();
+ }
+
+ public double getPartialRadiusY() {
+ double dRdY = 0;
+ for (PointModel point : points) {
+ dRdY += point.getPartialDiY();
+ }
+ return dRdY / points.size();
+ }
+
+ public double getRadius() {
+ double r = 0;
+ for (PointModel point : points) {
+ r += point.getCenterDistance();
+ }
+ return r / points.size();
+ }
+
+ public double getX() {
+ return cx.getEstimate();
+ }
+
+ public double getY() {
+ return cy.getEstimate();
+ }
+
+ private static class PointModel extends WeightedMeasurement {
+
+ public PointModel(Circle circle, double px, double py) {
+ super(1.0, 0.0);
+ this.px = px;
+ this.py = py;
+ this.circle = circle;
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ if (parameter == circle.cx) {
+ return getPartialDiX() - circle.getPartialRadiusX();
+ } else if (parameter == circle.cy) {
+ return getPartialDiY() - circle.getPartialRadiusY();
+ }
+ return 0;
+ }
+
+ public double getCenterDistance() {
+ double dx = px - circle.cx.getEstimate();
+ double dy = py - circle.cy.getEstimate();
+ return FastMath.sqrt(dx * dx + dy * dy);
+ }
+
+ public double getPartialDiX() {
+ return (circle.cx.getEstimate() - px) / getCenterDistance();
+ }
+
+ public double getPartialDiY() {
+ return (circle.cy.getEstimate() - py) / getCenterDistance();
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ return getCenterDistance() - circle.getRadius();
+ }
+
+ private double px;
+ private double py;
+ private transient final Circle circle;
+ private static final long serialVersionUID = 1L;
+
+ }
+
+ private EstimatedParameter cx;
+ private EstimatedParameter cy;
+ private ArrayList<PointModel> points;
+
+ }
+
+ private static class QuadraticProblem extends SimpleEstimationProblem {
+
+ private EstimatedParameter a;
+ private EstimatedParameter b;
+ private EstimatedParameter c;
+
+ public QuadraticProblem() {
+ a = new EstimatedParameter("a", 0.0);
+ b = new EstimatedParameter("b", 0.0);
+ c = new EstimatedParameter("c", 0.0);
+ addParameter(a);
+ addParameter(b);
+ addParameter(c);
+ }
+
+ public void addPoint(double x, double y, double w) {
+ addMeasurement(new LocalMeasurement(this, x, y, w));
+ }
+
+ public double theoreticalValue(double x) {
+ return ( (a.getEstimate() * x + b.getEstimate() ) * x + c.getEstimate());
+ }
+
+ private double partial(double x, EstimatedParameter parameter) {
+ if (parameter == a) {
+ return x * x;
+ } else if (parameter == b) {
+ return x;
+ } else {
+ return 1.0;
+ }
+ }
+
+ private static class LocalMeasurement extends WeightedMeasurement {
+
+ private static final long serialVersionUID = 1555043155023729130L;
+ private final double x;
+ private transient final QuadraticProblem pb;
+
+ // constructor
+ public LocalMeasurement(QuadraticProblem pb, double x, double y, double w) {
+ super(w, y);
+ this.x = x;
+ this.pb = pb;
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ return pb.theoreticalValue(x);
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ return pb.partial(x, parameter);
+ }
+
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/estimation/MinpackTest.java b/src/test/java/org/apache/commons/math/estimation/MinpackTest.java
new file mode 100644
index 0000000..c66dafb
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/estimation/MinpackTest.java
@@ -0,0 +1,1538 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.estimation.EstimatedParameter;
+import org.apache.commons.math.estimation.EstimationException;
+import org.apache.commons.math.estimation.EstimationProblem;
+import org.apache.commons.math.estimation.LevenbergMarquardtEstimator;
+import org.apache.commons.math.estimation.WeightedMeasurement;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+ at Deprecated
+public class MinpackTest
+ extends TestCase {
+
+ public MinpackTest(String name) {
+ super(name);
+ }
+
+ public void testMinpackLinearFullRank() {
+ minpackTest(new LinearFullRankFunction(10, 5, 1.0,
+ 5.0, 2.23606797749979), false);
+ minpackTest(new LinearFullRankFunction(50, 5, 1.0,
+ 8.06225774829855, 6.70820393249937), false);
+ }
+
+ public void testMinpackLinearRank1() {
+ minpackTest(new LinearRank1Function(10, 5, 1.0,
+ 291.521868819476, 1.4638501094228), false);
+ minpackTest(new LinearRank1Function(50, 5, 1.0,
+ 3101.60039334535, 3.48263016573496), false);
+ }
+
+ public void testMinpackLinearRank1ZeroColsAndRows() {
+ minpackTest(new LinearRank1ZeroColsAndRowsFunction(10, 5, 1.0), false);
+ minpackTest(new LinearRank1ZeroColsAndRowsFunction(50, 5, 1.0), false);
+ }
+
+ public void testMinpackRosenbrok() {
+ minpackTest(new RosenbrockFunction(new double[] { -1.2, 1.0 },
+ FastMath.sqrt(24.2)), false);
+ minpackTest(new RosenbrockFunction(new double[] { -12.0, 10.0 },
+ FastMath.sqrt(1795769.0)), false);
+ minpackTest(new RosenbrockFunction(new double[] { -120.0, 100.0 },
+ 11.0 * FastMath.sqrt(169000121.0)), false);
+ }
+
+ public void testMinpackHelicalValley() {
+ minpackTest(new HelicalValleyFunction(new double[] { -1.0, 0.0, 0.0 },
+ 50.0), false);
+ minpackTest(new HelicalValleyFunction(new double[] { -10.0, 0.0, 0.0 },
+ 102.95630140987), false);
+ minpackTest(new HelicalValleyFunction(new double[] { -100.0, 0.0, 0.0},
+ 991.261822123701), false);
+ }
+
+ public void testMinpackPowellSingular() {
+ minpackTest(new PowellSingularFunction(new double[] { 3.0, -1.0, 0.0, 1.0 },
+ 14.6628782986152), false);
+ minpackTest(new PowellSingularFunction(new double[] { 30.0, -10.0, 0.0, 10.0 },
+ 1270.9838708654), false);
+ minpackTest(new PowellSingularFunction(new double[] { 300.0, -100.0, 0.0, 100.0 },
+ 126887.903284750), false);
+ }
+
+ public void testMinpackFreudensteinRoth() {
+ minpackTest(new FreudensteinRothFunction(new double[] { 0.5, -2.0 },
+ 20.0124960961895, 6.99887517584575,
+ new double[] {
+ 11.4124844654993,
+ -0.896827913731509
+ }), false);
+ minpackTest(new FreudensteinRothFunction(new double[] { 5.0, -20.0 },
+ 12432.833948863, 6.9988751744895,
+ new double[] {
+ 11.4130046614746,
+ -0.896796038685958
+ }), false);
+ minpackTest(new FreudensteinRothFunction(new double[] { 50.0, -200.0 },
+ 11426454.595762, 6.99887517242903,
+ new double[] {
+ 11.4127817857886,
+ -0.89680510749204
+ }), false);
+ }
+
+ public void testMinpackBard() {
+ minpackTest(new BardFunction(1.0, 6.45613629515967, 0.0906359603390466,
+ new double[] {
+ 0.0824105765758334,
+ 1.1330366534715,
+ 2.34369463894115
+ }), false);
+ minpackTest(new BardFunction(10.0, 36.1418531596785, 4.17476870138539,
+ new double[] {
+ 0.840666673818329,
+ -158848033.259565,
+ -164378671.653535
+ }), false);
+ minpackTest(new BardFunction(100.0, 384.114678637399, 4.17476870135969,
+ new double[] {
+ 0.840666673867645,
+ -158946167.205518,
+ -164464906.857771
+ }), false);
+ }
+
+ public void testMinpackKowalikOsborne() {
+ minpackTest(new KowalikOsborneFunction(new double[] { 0.25, 0.39, 0.415, 0.39 },
+ 0.0728915102882945,
+ 0.017535837721129,
+ new double[] {
+ 0.192807810476249,
+ 0.191262653354071,
+ 0.123052801046931,
+ 0.136053221150517
+ }), false);
+ minpackTest(new KowalikOsborneFunction(new double[] { 2.5, 3.9, 4.15, 3.9 },
+ 2.97937007555202,
+ 0.032052192917937,
+ new double[] {
+ 728675.473768287,
+ -14.0758803129393,
+ -32977797.7841797,
+ -20571594.1977912
+ }), false);
+ minpackTest(new KowalikOsborneFunction(new double[] { 25.0, 39.0, 41.5, 39.0 },
+ 29.9590617016037,
+ 0.0175364017658228,
+ new double[] {
+ 0.192948328597594,
+ 0.188053165007911,
+ 0.122430604321144,
+ 0.134575665392506
+ }), true);
+ }
+
+ public void testMinpackMeyer() {
+ minpackTest(new MeyerFunction(new double[] { 0.02, 4000.0, 250.0 },
+ 41153.4665543031, 9.37794514651874,
+ new double[] {
+ 0.00560963647102661,
+ 6181.34634628659,
+ 345.223634624144
+ }), false);
+ minpackTest(new MeyerFunction(new double[] { 0.2, 40000.0, 2500.0 },
+ 4168216.89130846, 792.917871779501,
+ new double[] {
+ 1.42367074157994e-11,
+ 33695.7133432541,
+ 901.268527953801
+ }), true);
+ }
+
+ public void testMinpackWatson() {
+
+ minpackTest(new WatsonFunction(6, 0.0,
+ 5.47722557505166, 0.0478295939097601,
+ new double[] {
+ -0.0157249615083782, 1.01243488232965,
+ -0.232991722387673, 1.26043101102818,
+ -1.51373031394421, 0.99299727291842
+ }), false);
+ minpackTest(new WatsonFunction(6, 10.0,
+ 6433.12578950026, 0.0478295939096951,
+ new double[] {
+ -0.0157251901386677, 1.01243485860105,
+ -0.232991545843829, 1.26042932089163,
+ -1.51372776706575, 0.99299573426328
+ }), false);
+ minpackTest(new WatsonFunction(6, 100.0,
+ 674256.040605213, 0.047829593911544,
+ new double[] {
+ -0.0157247019712586, 1.01243490925658,
+ -0.232991922761641, 1.26043292929555,
+ -1.51373320452707, 0.99299901922322
+ }), false);
+
+ minpackTest(new WatsonFunction(9, 0.0,
+ 5.47722557505166, 0.00118311459212420,
+ new double[] {
+ -0.153070644166722e-4, 0.999789703934597,
+ 0.0147639634910978, 0.146342330145992,
+ 1.00082109454817, -2.61773112070507,
+ 4.10440313943354, -3.14361226236241,
+ 1.05262640378759
+ }), false);
+ minpackTest(new WatsonFunction(9, 10.0,
+ 12088.127069307, 0.00118311459212513,
+ new double[] {
+ -0.153071334849279e-4, 0.999789703941234,
+ 0.0147639629786217, 0.146342334818836,
+ 1.00082107321386, -2.61773107084722,
+ 4.10440307655564, -3.14361222178686,
+ 1.05262639322589
+ }), false);
+ minpackTest(new WatsonFunction(9, 100.0,
+ 1269109.29043834, 0.00118311459212384,
+ new double[] {
+ -0.153069523352176e-4, 0.999789703958371,
+ 0.0147639625185392, 0.146342341096326,
+ 1.00082104729164, -2.61773101573645,
+ 4.10440301427286, -3.14361218602503,
+ 1.05262638516774
+ }), false);
+
+ minpackTest(new WatsonFunction(12, 0.0,
+ 5.47722557505166, 0.217310402535861e-4,
+ new double[] {
+ -0.660266001396382e-8, 1.00000164411833,
+ -0.000563932146980154, 0.347820540050756,
+ -0.156731500244233, 1.05281515825593,
+ -3.24727109519451, 7.2884347837505,
+ -10.271848098614, 9.07411353715783,
+ -4.54137541918194, 1.01201187975044
+ }), false);
+ minpackTest(new WatsonFunction(12, 10.0,
+ 19220.7589790951, 0.217310402518509e-4,
+ new double[] {
+ -0.663710223017410e-8, 1.00000164411787,
+ -0.000563932208347327, 0.347820540486998,
+ -0.156731503955652, 1.05281517654573,
+ -3.2472711515214, 7.28843489430665,
+ -10.2718482369638, 9.07411364383733,
+ -4.54137546533666, 1.01201188830857
+ }), false);
+ minpackTest(new WatsonFunction(12, 100.0,
+ 2018918.04462367, 0.217310402539845e-4,
+ new double[] {
+ -0.663806046485249e-8, 1.00000164411786,
+ -0.000563932210324959, 0.347820540503588,
+ -0.156731504091375, 1.05281517718031,
+ -3.24727115337025, 7.28843489775302,
+ -10.2718482410813, 9.07411364688464,
+ -4.54137546660822, 1.0120118885369
+ }), false);
+
+ }
+
+ public void testMinpackBox3Dimensional() {
+ minpackTest(new Box3DimensionalFunction(10, new double[] { 0.0, 10.0, 20.0 },
+ 32.1115837449572), false);
+ }
+
+ public void testMinpackJennrichSampson() {
+ minpackTest(new JennrichSampsonFunction(10, new double[] { 0.3, 0.4 },
+ 64.5856498144943, 11.1517793413499,
+ new double[] {
+ 0.257819926636811, 0.257829976764542
+ }), false);
+ }
+
+ public void testMinpackBrownDennis() {
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 25.0, 5.0, -5.0, -1.0 },
+ 2815.43839161816, 292.954288244866,
+ new double[] {
+ -11.59125141003, 13.2024883984741,
+ -0.403574643314272, 0.236736269844604
+ }), false);
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 250.0, 50.0, -50.0, -10.0 },
+ 555073.354173069, 292.954270581415,
+ new double[] {
+ -11.5959274272203, 13.2041866926242,
+ -0.403417362841545, 0.236771143410386
+ }), false);
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 2500.0, 500.0, -500.0, -100.0 },
+ 61211252.2338581, 292.954306151134,
+ new double[] {
+ -11.5902596937374, 13.2020628854665,
+ -0.403688070279258, 0.236665033746463
+ }), false);
+ }
+
+ public void testMinpackChebyquad() {
+ minpackTest(new ChebyquadFunction(1, 8, 1.0,
+ 1.88623796907732, 1.88623796907732,
+ new double[] { 0.5 }), false);
+ minpackTest(new ChebyquadFunction(1, 8, 10.0,
+ 5383344372.34005, 1.88424820499951,
+ new double[] { 0.9817314924684 }), false);
+ minpackTest(new ChebyquadFunction(1, 8, 100.0,
+ 0.118088726698392e19, 1.88424820499347,
+ new double[] { 0.9817314852934 }), false);
+ minpackTest(new ChebyquadFunction(8, 8, 1.0,
+ 0.196513862833975, 0.0593032355046727,
+ new double[] {
+ 0.0431536648587336, 0.193091637843267,
+ 0.266328593812698, 0.499999334628884,
+ 0.500000665371116, 0.733671406187302,
+ 0.806908362156733, 0.956846335141266
+ }), false);
+ minpackTest(new ChebyquadFunction(9, 9, 1.0,
+ 0.16994993465202, 0.0,
+ new double[] {
+ 0.0442053461357828, 0.199490672309881,
+ 0.23561910847106, 0.416046907892598,
+ 0.5, 0.583953092107402,
+ 0.764380891528940, 0.800509327690119,
+ 0.955794653864217
+ }), false);
+ minpackTest(new ChebyquadFunction(10, 10, 1.0,
+ 0.183747831178711, 0.0806471004038253,
+ new double[] {
+ 0.0596202671753563, 0.166708783805937,
+ 0.239171018813509, 0.398885290346268,
+ 0.398883667870681, 0.601116332129320,
+ 0.60111470965373, 0.760828981186491,
+ 0.833291216194063, 0.940379732824644
+ }), false);
+ }
+
+ public void testMinpackBrownAlmostLinear() {
+ minpackTest(new BrownAlmostLinearFunction(10, 0.5,
+ 16.5302162063499, 0.0,
+ new double[] {
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 1.20569696650138
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(10, 5.0,
+ 9765624.00089211, 0.0,
+ new double[] {
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 1.20569696650135
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(10, 50.0,
+ 0.9765625e17, 0.0,
+ new double[] {
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(30, 0.5,
+ 83.476044467848, 0.0,
+ new double[] {
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 1.06737350671578
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(40, 0.5,
+ 128.026364472323, 0.0,
+ new double[] {
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 0.999999999999121
+ }), false);
+ }
+
+ public void testMinpackOsborne1() {
+ minpackTest(new Osborne1Function(new double[] { 0.5, 1.5, -1.0, 0.01, 0.02, },
+ 0.937564021037838, 0.00739249260904843,
+ new double[] {
+ 0.375410049244025, 1.93584654543108,
+ -1.46468676748716, 0.0128675339110439,
+ 0.0221227011813076
+ }), false);
+ }
+
+ public void testMinpackOsborne2() {
+
+ minpackTest(new Osborne2Function(new double[] {
+ 1.3, 0.65, 0.65, 0.7, 0.6,
+ 3.0, 5.0, 7.0, 2.0, 4.5, 5.5
+ },
+ 1.44686540984712, 0.20034404483314,
+ new double[] {
+ 1.30997663810096, 0.43155248076,
+ 0.633661261602859, 0.599428560991695,
+ 0.754179768272449, 0.904300082378518,
+ 1.36579949521007, 4.82373199748107,
+ 2.39868475104871, 4.56887554791452,
+ 5.67534206273052
+ }), false);
+ }
+
+ private void minpackTest(MinpackFunction function, boolean exceptionExpected) {
+ LevenbergMarquardtEstimator estimator = new LevenbergMarquardtEstimator();
+ estimator.setMaxCostEval(100 * (function.getN() + 1));
+ estimator.setCostRelativeTolerance(FastMath.sqrt(2.22044604926e-16));
+ estimator.setParRelativeTolerance(FastMath.sqrt(2.22044604926e-16));
+ estimator.setOrthoTolerance(2.22044604926e-16);
+ assertTrue(function.checkTheoreticalStartCost(estimator.getRMS(function)));
+ try {
+ estimator.estimate(function);
+ assertFalse(exceptionExpected);
+ } catch (EstimationException lsse) {
+ assertTrue(exceptionExpected);
+ }
+ assertTrue(function.checkTheoreticalMinCost(estimator.getRMS(function)));
+ assertTrue(function.checkTheoreticalMinParams());
+ }
+
+ private static abstract class MinpackFunction implements EstimationProblem {
+
+ protected MinpackFunction(int m,
+ double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ this.m = m;
+ this.n = startParams.length;
+ parameters = new EstimatedParameter[n];
+ for (int i = 0; i < n; ++i) {
+ parameters[i] = new EstimatedParameter("p" + i, startParams[i]);
+ }
+ this.theoreticalStartCost = theoreticalStartCost;
+ this.theoreticalMinCost = theoreticalMinCost;
+ this.theoreticalMinParams = theoreticalMinParams;
+ this.costAccuracy = 1.0e-8;
+ this.paramsAccuracy = 1.0e-5;
+ }
+
+ protected static double[] buildArray(int n, double x) {
+ double[] array = new double[n];
+ Arrays.fill(array, x);
+ return array;
+ }
+
+ protected void setCostAccuracy(double costAccuracy) {
+ this.costAccuracy = costAccuracy;
+ }
+
+ protected void setParamsAccuracy(double paramsAccuracy) {
+ this.paramsAccuracy = paramsAccuracy;
+ }
+
+ public int getN() {
+ return parameters.length;
+ }
+
+ public boolean checkTheoreticalStartCost(double rms) {
+ double threshold = costAccuracy * (1.0 + theoreticalStartCost);
+ return FastMath.abs(FastMath.sqrt(m) * rms - theoreticalStartCost) <= threshold;
+ }
+
+ public boolean checkTheoreticalMinCost(double rms) {
+ double threshold = costAccuracy * (1.0 + theoreticalMinCost);
+ return FastMath.abs(FastMath.sqrt(m) * rms - theoreticalMinCost) <= threshold;
+ }
+
+ public boolean checkTheoreticalMinParams() {
+ if (theoreticalMinParams != null) {
+ for (int i = 0; i < theoreticalMinParams.length; ++i) {
+ double mi = theoreticalMinParams[i];
+ double vi = parameters[i].getEstimate();
+ if (FastMath.abs(mi - vi) > (paramsAccuracy * (1.0 + FastMath.abs(mi)))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public WeightedMeasurement[] getMeasurements() {
+ WeightedMeasurement[] measurements = new WeightedMeasurement[m];
+ for (int i = 0; i < m; ++i) {
+ measurements[i] = new MinpackMeasurement(this, i);
+ }
+ return measurements;
+ }
+
+ public EstimatedParameter[] getUnboundParameters() {
+ return parameters;
+ }
+
+ public EstimatedParameter[] getAllParameters() {
+ return parameters;
+ }
+
+ protected abstract double[][] getJacobian();
+
+ protected abstract double[] getResiduals();
+
+ private static class MinpackMeasurement extends WeightedMeasurement {
+
+ public MinpackMeasurement(MinpackFunction f, int index) {
+ super(1.0, 0.0);
+ this.index = index;
+ this.f = f;
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ // this is obviously NOT efficient as we recompute the whole vector
+ // each time we need only one element, but it is only for test
+ // purposes and is simpler to check.
+ // This implementation should NOT be taken as an example, it is ugly!
+ return f.getResiduals()[index];
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter parameter) {
+ // this is obviously NOT efficient as we recompute the whole jacobian
+ // each time we need only one element, but it is only for test
+ // purposes and is simpler to check.
+ // This implementation should NOT be taken as an example, it is ugly!
+ for (int j = 0; j < f.n; ++j) {
+ if (parameter == f.parameters[j]) {
+ return f.getJacobian()[index][j];
+ }
+ }
+ return 0;
+ }
+
+ private int index;
+ private transient final MinpackFunction f;
+ private static final long serialVersionUID = 1L;
+
+ }
+
+ protected int n;
+ protected int m;
+ protected EstimatedParameter[] parameters;
+ protected double theoreticalStartCost;
+ protected double theoreticalMinCost;
+ protected double[] theoreticalMinParams;
+ protected double costAccuracy;
+ protected double paramsAccuracy;
+
+ }
+
+ private static class LinearFullRankFunction extends MinpackFunction {
+
+ public LinearFullRankFunction(int m, int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost) {
+ super(m, buildArray(n, x0), theoreticalStartCost,
+ theoreticalMinCost, buildArray(n, -1.0));
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double t = 2.0 / m;
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = (i == j) ? (1 - t) : -t;
+ }
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += parameters[i].getEstimate();
+ }
+ double t = 1 + 2 * sum / m;
+ double[] f = new double[m];
+ for (int i = 0; i < n; ++i) {
+ f[i] = parameters[i].getEstimate() - t;
+ }
+ Arrays.fill(f, n, m, -t);
+ return f;
+ }
+
+ }
+
+ private static class LinearRank1Function extends MinpackFunction {
+
+ public LinearRank1Function(int m, int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost) {
+ super(m, buildArray(n, x0), theoreticalStartCost, theoreticalMinCost, null);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = (i + 1) * (j + 1);
+ }
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double[] f = new double[m];
+ double sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += (i + 1) * parameters[i].getEstimate();
+ }
+ for (int i = 0; i < m; ++i) {
+ f[i] = (i + 1) * sum - 1;
+ }
+ return f;
+ }
+
+ }
+
+ private static class LinearRank1ZeroColsAndRowsFunction extends MinpackFunction {
+
+ public LinearRank1ZeroColsAndRowsFunction(int m, int n, double x0) {
+ super(m, buildArray(n, x0),
+ FastMath.sqrt(m + (n+1)*(n-2)*(m-2)*(m-1) * ((n+1)*(n-2)*(2*m-3) - 12) / 24.0),
+ FastMath.sqrt((m * (m + 3) - 6) / (2.0 * (2 * m - 3))),
+ null);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ jacobian[i][0] = 0;
+ for (int j = 1; j < (n - 1); ++j) {
+ if (i == 0) {
+ jacobian[i][j] = 0;
+ } else if (i != (m - 1)) {
+ jacobian[i][j] = i * (j + 1);
+ } else {
+ jacobian[i][j] = 0;
+ }
+ }
+ jacobian[i][n - 1] = 0;
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double[] f = new double[m];
+ double sum = 0;
+ for (int i = 1; i < (n - 1); ++i) {
+ sum += (i + 1) * parameters[i].getEstimate();
+ }
+ for (int i = 0; i < (m - 1); ++i) {
+ f[i] = i * sum - 1;
+ }
+ f[m - 1] = -1;
+ return f;
+ }
+
+ }
+
+ private static class RosenbrockFunction extends MinpackFunction {
+
+ public RosenbrockFunction(double[] startParams, double theoreticalStartCost) {
+ super(2, startParams, theoreticalStartCost, 0.0, buildArray(2, 1.0));
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ return new double[][] { { -20 * x1, 10 }, { -1, 0 } };
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ return new double[] { 10 * (x2 - x1 * x1), 1 - x1 };
+ }
+
+ }
+
+ private static class HelicalValleyFunction extends MinpackFunction {
+
+ public HelicalValleyFunction(double[] startParams,
+ double theoreticalStartCost) {
+ super(3, startParams, theoreticalStartCost, 0.0,
+ new double[] { 1.0, 0.0, 0.0 });
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double tmpSquare = x1 * x1 + x2 * x2;
+ double tmp1 = twoPi * tmpSquare;
+ double tmp2 = FastMath.sqrt(tmpSquare);
+ return new double[][] {
+ { 100 * x2 / tmp1, -100 * x1 / tmp1, 10 },
+ { 10 * x1 / tmp2, 10 * x2 / tmp2, 0 },
+ { 0, 0, 1 }
+ };
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double tmp1;
+ if (x1 == 0) {
+ tmp1 = (x2 >= 0) ? 0.25 : -0.25;
+ } else {
+ tmp1 = FastMath.atan(x2 / x1) / twoPi;
+ if (x1 < 0) {
+ tmp1 += 0.5;
+ }
+ }
+ double tmp2 = FastMath.sqrt(x1 * x1 + x2 * x2);
+ return new double[] {
+ 10.0 * (x3 - 10 * tmp1),
+ 10.0 * (tmp2 - 1),
+ x3
+ };
+ }
+
+ private static final double twoPi = 2.0 * FastMath.PI;
+
+ }
+
+ private static class PowellSingularFunction extends MinpackFunction {
+
+ public PowellSingularFunction(double[] startParams,
+ double theoreticalStartCost) {
+ super(4, startParams, theoreticalStartCost, 0.0, buildArray(4, 0.0));
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ return new double[][] {
+ { 1, 10, 0, 0 },
+ { 0, 0, sqrt5, -sqrt5 },
+ { 0, 2 * (x2 - 2 * x3), -4 * (x2 - 2 * x3), 0 },
+ { 2 * sqrt10 * (x1 - x4), 0, 0, -2 * sqrt10 * (x1 - x4) }
+ };
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ return new double[] {
+ x1 + 10 * x2,
+ sqrt5 * (x3 - x4),
+ (x2 - 2 * x3) * (x2 - 2 * x3),
+ sqrt10 * (x1 - x4) * (x1 - x4)
+ };
+ }
+
+ private static final double sqrt5 = FastMath.sqrt( 5.0);
+ private static final double sqrt10 = FastMath.sqrt(10.0);
+
+ }
+
+ private static class FreudensteinRothFunction extends MinpackFunction {
+
+ public FreudensteinRothFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(2, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x2 = parameters[1].getEstimate();
+ return new double[][] {
+ { 1, x2 * (10 - 3 * x2) - 2 },
+ { 1, x2 * ( 2 + 3 * x2) - 14, }
+ };
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ return new double[] {
+ -13.0 + x1 + ((5.0 - x2) * x2 - 2.0) * x2,
+ -29.0 + x1 + ((1.0 + x2) * x2 - 14.0) * x2
+ };
+ }
+
+ }
+
+ private static class BardFunction extends MinpackFunction {
+
+ public BardFunction(double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(15, buildArray(3, x0), theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp1 = i + 1;
+ double tmp2 = 15 - i;
+ double tmp3 = (i <= 7) ? tmp1 : tmp2;
+ double tmp4 = x2 * tmp2 + x3 * tmp3;
+ tmp4 *= tmp4;
+ jacobian[i] = new double[] { -1, tmp1 * tmp2 / tmp4, tmp1 * tmp3 / tmp4 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double tmp1 = i + 1;
+ double tmp2 = 15 - i;
+ double tmp3 = (i <= 7) ? tmp1 : tmp2;
+ f[i] = y[i] - (x1 + tmp1 / (x2 * tmp2 + x3 * tmp3));
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 0.14, 0.18, 0.22, 0.25, 0.29,
+ 0.32, 0.35, 0.39, 0.37, 0.58,
+ 0.73, 0.96, 1.34, 2.10, 4.39
+ };
+
+ }
+
+ private static class KowalikOsborneFunction extends MinpackFunction {
+
+ public KowalikOsborneFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(11, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ if (theoreticalStartCost > 20.0) {
+ setCostAccuracy(2.0e-4);
+ setParamsAccuracy(5.0e-3);
+ }
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp = v[i] * (v[i] + x3) + x4;
+ double j1 = -v[i] * (v[i] + x2) / tmp;
+ double j2 = -v[i] * x1 / tmp;
+ double j3 = j1 * j2;
+ double j4 = j3 / v[i];
+ jacobian[i] = new double[] { j1, j2, j3, j4 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ f[i] = y[i] - x1 * (v[i] * (v[i] + x2)) / (v[i] * (v[i] + x3) + x4);
+ }
+ return f;
+ }
+
+ private static final double[] v = {
+ 4.0, 2.0, 1.0, 0.5, 0.25, 0.167, 0.125, 0.1, 0.0833, 0.0714, 0.0625
+ };
+
+ private static final double[] y = {
+ 0.1957, 0.1947, 0.1735, 0.1600, 0.0844, 0.0627,
+ 0.0456, 0.0342, 0.0323, 0.0235, 0.0246
+ };
+
+ }
+
+ private static class MeyerFunction extends MinpackFunction {
+
+ public MeyerFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(16, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ if (theoreticalStartCost > 1.0e6) {
+ setCostAccuracy(7.0e-3);
+ setParamsAccuracy(2.0e-2);
+ }
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = 5.0 * (i + 1) + 45.0 + x3;
+ double tmp1 = x2 / temp;
+ double tmp2 = FastMath.exp(tmp1);
+ double tmp3 = x1 * tmp2 / temp;
+ jacobian[i] = new double[] { tmp2, tmp3, -tmp1 * tmp3 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ f[i] = x1 * FastMath.exp(x2 / (5.0 * (i + 1) + 45.0 + x3)) - y[i];
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 34780.0, 28610.0, 23650.0, 19630.0,
+ 16370.0, 13720.0, 11540.0, 9744.0,
+ 8261.0, 7030.0, 6005.0, 5147.0,
+ 4427.0, 3820.0, 3307.0, 2872.0
+ };
+
+ }
+
+ private static class WatsonFunction extends MinpackFunction {
+
+ public WatsonFunction(int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(31, buildArray(n, x0), theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+
+ double[][] jacobian = new double[m][];
+
+ for (int i = 0; i < (m - 2); ++i) {
+ double div = (i + 1) / 29.0;
+ double s2 = 0.0;
+ double dx = 1.0;
+ for (int j = 0; j < n; ++j) {
+ s2 += dx * parameters[j].getEstimate();
+ dx *= div;
+ }
+ double temp= 2 * div * s2;
+ dx = 1.0 / div;
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = dx * (j - temp);
+ dx *= div;
+ }
+ }
+
+ jacobian[m - 2] = new double[n];
+ jacobian[m - 2][0] = 1;
+
+ jacobian[m - 1] = new double[n];
+ jacobian[m - 1][0]= -2 * parameters[0].getEstimate();
+ jacobian[m - 1][1]= 1;
+
+ return jacobian;
+
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double[] f = new double[m];
+ for (int i = 0; i < (m - 2); ++i) {
+ double div = (i + 1) / 29.0;
+ double s1 = 0;
+ double dx = 1;
+ for (int j = 1; j < n; ++j) {
+ s1 += j * dx * parameters[j].getEstimate();
+ dx *= div;
+ }
+ double s2 =0;
+ dx =1;
+ for (int j = 0; j < n; ++j) {
+ s2 += dx * parameters[j].getEstimate();
+ dx *= div;
+ }
+ f[i] = s1 - s2 * s2 - 1;
+ }
+
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ f[m - 2] = x1;
+ f[m - 1] = x2 - x1 * x1 - 1;
+
+ return f;
+
+ }
+
+ }
+
+ private static class Box3DimensionalFunction extends MinpackFunction {
+
+ public Box3DimensionalFunction(int m, double[] startParams,
+ double theoreticalStartCost) {
+ super(m, startParams, theoreticalStartCost,
+ 0.0, new double[] { 1.0, 10.0, 1.0 });
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp = (i + 1) / 10.0;
+ jacobian[i] = new double[] {
+ -tmp * FastMath.exp(-tmp * x1),
+ tmp * FastMath.exp(-tmp * x2),
+ FastMath.exp(-i - 1) - FastMath.exp(-tmp)
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double tmp = (i + 1) / 10.0;
+ f[i] = FastMath.exp(-tmp * x1) - FastMath.exp(-tmp * x2)
+ + (FastMath.exp(-i - 1) - FastMath.exp(-tmp)) * x3;
+ }
+ return f;
+ }
+
+ }
+
+ private static class JennrichSampsonFunction extends MinpackFunction {
+
+ public JennrichSampsonFunction(int m, double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double t = i + 1;
+ jacobian[i] = new double[] { -t * FastMath.exp(t * x1), -t * FastMath.exp(t * x2) };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = i + 1;
+ f[i] = 2 + 2 * temp - FastMath.exp(temp * x1) - FastMath.exp(temp * x2);
+ }
+ return f;
+ }
+
+ }
+
+ private static class BrownDennisFunction extends MinpackFunction {
+
+ public BrownDennisFunction(int m, double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ setCostAccuracy(2.5e-8);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = (i + 1) / 5.0;
+ double ti = FastMath.sin(temp);
+ double tmp1 = x1 + temp * x2 - FastMath.exp(temp);
+ double tmp2 = x3 + ti * x4 - FastMath.cos(temp);
+ jacobian[i] = new double[] {
+ 2 * tmp1, 2 * temp * tmp1, 2 * tmp2, 2 * ti * tmp2
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = (i + 1) / 5.0;
+ double tmp1 = x1 + temp * x2 - FastMath.exp(temp);
+ double tmp2 = x3 + FastMath.sin(temp) * x4 - FastMath.cos(temp);
+ f[i] = tmp1 * tmp1 + tmp2 * tmp2;
+ }
+ return f;
+ }
+
+ }
+
+ private static class ChebyquadFunction extends MinpackFunction {
+
+ private static double[] buildChebyquadArray(int n, double factor) {
+ double[] array = new double[n];
+ double inv = factor / (n + 1);
+ for (int i = 0; i < n; ++i) {
+ array[i] = (i + 1) * inv;
+ }
+ return array;
+ }
+
+ public ChebyquadFunction(int n, int m, double factor,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, buildChebyquadArray(n, factor), theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ }
+
+ double dx = 1.0 / n;
+ for (int j = 0; j < n; ++j) {
+ double tmp1 = 1;
+ double tmp2 = 2 * parameters[j].getEstimate() - 1;
+ double temp = 2 * tmp2;
+ double tmp3 = 0;
+ double tmp4 = 2;
+ for (int i = 0; i < m; ++i) {
+ jacobian[i][j] = dx * tmp4;
+ double ti = 4 * tmp2 + temp * tmp4 - tmp3;
+ tmp3 = tmp4;
+ tmp4 = ti;
+ ti = temp * tmp2 - tmp1;
+ tmp1 = tmp2;
+ tmp2 = ti;
+ }
+ }
+
+ return jacobian;
+
+ }
+
+ @Override
+ protected double[] getResiduals() {
+
+ double[] f = new double[m];
+
+ for (int j = 0; j < n; ++j) {
+ double tmp1 = 1;
+ double tmp2 = 2 * parameters[j].getEstimate() - 1;
+ double temp = 2 * tmp2;
+ for (int i = 0; i < m; ++i) {
+ f[i] += tmp2;
+ double ti = temp * tmp2 - tmp1;
+ tmp1 = tmp2;
+ tmp2 = ti;
+ }
+ }
+
+ double dx = 1.0 / n;
+ boolean iev = false;
+ for (int i = 0; i < m; ++i) {
+ f[i] *= dx;
+ if (iev) {
+ f[i] += 1.0 / (i * (i + 2));
+ }
+ iev = ! iev;
+ }
+
+ return f;
+
+ }
+
+ }
+
+ private static class BrownAlmostLinearFunction extends MinpackFunction {
+
+ public BrownAlmostLinearFunction(int m, double factor,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, buildArray(m, factor), theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ }
+
+ double prod = 1;
+ for (int j = 0; j < n; ++j) {
+ prod *= parameters[j].getEstimate();
+ for (int i = 0; i < n; ++i) {
+ jacobian[i][j] = 1;
+ }
+ jacobian[j][j] = 2;
+ }
+
+ for (int j = 0; j < n; ++j) {
+ EstimatedParameter vj = parameters[j];
+ double temp = vj.getEstimate();
+ if (temp == 0) {
+ temp = 1;
+ prod = 1;
+ for (int k = 0; k < n; ++k) {
+ if (k != j) {
+ prod *= parameters[k].getEstimate();
+ }
+ }
+ }
+ jacobian[n - 1][j] = prod / temp;
+ }
+
+ return jacobian;
+
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double[] f = new double[m];
+ double sum = -(n + 1);
+ double prod = 1;
+ for (int j = 0; j < n; ++j) {
+ sum += parameters[j].getEstimate();
+ prod *= parameters[j].getEstimate();
+ }
+ for (int i = 0; i < n; ++i) {
+ f[i] = parameters[i].getEstimate() + sum;
+ }
+ f[n - 1] = prod - 1;
+ return f;
+ }
+
+ }
+
+ private static class Osborne1Function extends MinpackFunction {
+
+ public Osborne1Function(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(33, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double x5 = parameters[4].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = 10.0 * i;
+ double tmp1 = FastMath.exp(-temp * x4);
+ double tmp2 = FastMath.exp(-temp * x5);
+ jacobian[i] = new double[] {
+ -1, -tmp1, -tmp2, temp * x2 * tmp1, temp * x3 * tmp2
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x1 = parameters[0].getEstimate();
+ double x2 = parameters[1].getEstimate();
+ double x3 = parameters[2].getEstimate();
+ double x4 = parameters[3].getEstimate();
+ double x5 = parameters[4].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = 10.0 * i;
+ double tmp1 = FastMath.exp(-temp * x4);
+ double tmp2 = FastMath.exp(-temp * x5);
+ f[i] = y[i] - (x1 + x2 * tmp1 + x3 * tmp2);
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 0.844, 0.908, 0.932, 0.936, 0.925, 0.908, 0.881, 0.850, 0.818, 0.784, 0.751,
+ 0.718, 0.685, 0.658, 0.628, 0.603, 0.580, 0.558, 0.538, 0.522, 0.506, 0.490,
+ 0.478, 0.467, 0.457, 0.448, 0.438, 0.431, 0.424, 0.420, 0.414, 0.411, 0.406
+ };
+
+ }
+
+ private static class Osborne2Function extends MinpackFunction {
+
+ public Osborne2Function(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(65, startParams, theoreticalStartCost,
+ theoreticalMinCost, theoreticalMinParams);
+ }
+
+ @Override
+ protected double[][] getJacobian() {
+ double x01 = parameters[0].getEstimate();
+ double x02 = parameters[1].getEstimate();
+ double x03 = parameters[2].getEstimate();
+ double x04 = parameters[3].getEstimate();
+ double x05 = parameters[4].getEstimate();
+ double x06 = parameters[5].getEstimate();
+ double x07 = parameters[6].getEstimate();
+ double x08 = parameters[7].getEstimate();
+ double x09 = parameters[8].getEstimate();
+ double x10 = parameters[9].getEstimate();
+ double x11 = parameters[10].getEstimate();
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = i / 10.0;
+ double tmp1 = FastMath.exp(-x05 * temp);
+ double tmp2 = FastMath.exp(-x06 * (temp - x09) * (temp - x09));
+ double tmp3 = FastMath.exp(-x07 * (temp - x10) * (temp - x10));
+ double tmp4 = FastMath.exp(-x08 * (temp - x11) * (temp - x11));
+ jacobian[i] = new double[] {
+ -tmp1,
+ -tmp2,
+ -tmp3,
+ -tmp4,
+ temp * x01 * tmp1,
+ x02 * (temp - x09) * (temp - x09) * tmp2,
+ x03 * (temp - x10) * (temp - x10) * tmp3,
+ x04 * (temp - x11) * (temp - x11) * tmp4,
+ -2 * x02 * x06 * (temp - x09) * tmp2,
+ -2 * x03 * x07 * (temp - x10) * tmp3,
+ -2 * x04 * x08 * (temp - x11) * tmp4
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ protected double[] getResiduals() {
+ double x01 = parameters[0].getEstimate();
+ double x02 = parameters[1].getEstimate();
+ double x03 = parameters[2].getEstimate();
+ double x04 = parameters[3].getEstimate();
+ double x05 = parameters[4].getEstimate();
+ double x06 = parameters[5].getEstimate();
+ double x07 = parameters[6].getEstimate();
+ double x08 = parameters[7].getEstimate();
+ double x09 = parameters[8].getEstimate();
+ double x10 = parameters[9].getEstimate();
+ double x11 = parameters[10].getEstimate();
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = i / 10.0;
+ double tmp1 = FastMath.exp(-x05 * temp);
+ double tmp2 = FastMath.exp(-x06 * (temp - x09) * (temp - x09));
+ double tmp3 = FastMath.exp(-x07 * (temp - x10) * (temp - x10));
+ double tmp4 = FastMath.exp(-x08 * (temp - x11) * (temp - x11));
+ f[i] = y[i] - (x01 * tmp1 + x02 * tmp2 + x03 * tmp3 + x04 * tmp4);
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 1.366, 1.191, 1.112, 1.013, 0.991,
+ 0.885, 0.831, 0.847, 0.786, 0.725,
+ 0.746, 0.679, 0.608, 0.655, 0.616,
+ 0.606, 0.602, 0.626, 0.651, 0.724,
+ 0.649, 0.649, 0.694, 0.644, 0.624,
+ 0.661, 0.612, 0.558, 0.533, 0.495,
+ 0.500, 0.423, 0.395, 0.375, 0.372,
+ 0.391, 0.396, 0.405, 0.428, 0.429,
+ 0.523, 0.562, 0.607, 0.653, 0.672,
+ 0.708, 0.633, 0.668, 0.645, 0.632,
+ 0.591, 0.559, 0.597, 0.625, 0.739,
+ 0.710, 0.729, 0.720, 0.636, 0.581,
+ 0.428, 0.292, 0.162, 0.098, 0.054
+ };
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/estimation/WeightedMeasurementTest.java b/src/test/java/org/apache/commons/math/estimation/WeightedMeasurementTest.java
new file mode 100644
index 0000000..c20b0ce
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/estimation/WeightedMeasurementTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.estimation;
+
+import org.apache.commons.math.estimation.EstimatedParameter;
+import org.apache.commons.math.estimation.WeightedMeasurement;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+ at Deprecated
+public class WeightedMeasurementTest
+ extends TestCase {
+
+ public WeightedMeasurementTest(String name) {
+ super(name);
+ p1 = null;
+ p2 = null;
+ }
+
+ public void testConstruction() {
+ WeightedMeasurement m = new MyMeasurement(3.0, theoretical() + 0.1, this);
+ checkValue(m.getWeight(), 3.0);
+ checkValue(m.getMeasuredValue(), theoretical() + 0.1);
+ }
+
+ public void testIgnored() {
+ WeightedMeasurement m = new MyMeasurement(3.0, theoretical() + 0.1, this);
+ assertTrue(!m.isIgnored());
+ m.setIgnored(true);
+ assertTrue(m.isIgnored());
+ m.setIgnored(false);
+ assertTrue(!m.isIgnored());
+ }
+
+ public void testTheory() {
+ WeightedMeasurement m = new MyMeasurement(3.0, theoretical() + 0.1, this);
+ checkValue(m.getTheoreticalValue(), theoretical());
+ checkValue(m.getResidual(), 0.1);
+
+ double oldP1 = p1.getEstimate();
+ p1.setEstimate(oldP1 + m.getResidual() / m.getPartial(p1));
+ checkValue(m.getResidual(), 0.0);
+ p1.setEstimate(oldP1);
+ checkValue(m.getResidual(), 0.1);
+
+ double oldP2 = p2.getEstimate();
+ p2.setEstimate(oldP2 + m.getResidual() / m.getPartial(p2));
+ checkValue(m.getResidual(), 0.0);
+ p2.setEstimate(oldP2);
+ checkValue(m.getResidual(), 0.1);
+
+ }
+
+ @Override
+ public void setUp() {
+ p1 = new EstimatedParameter("p1", 1.0);
+ p2 = new EstimatedParameter("p2", 2.0);
+ }
+
+ @Override
+ public void tearDown() {
+ p1 = null;
+ p2 = null;
+ }
+
+ private void checkValue(double value, double expected) {
+ assertTrue(FastMath.abs(value - expected) < 1.0e-10);
+ }
+
+ private double theoretical() {
+ return 3 * p1.getEstimate() - p2.getEstimate();
+ }
+
+ private double partial(EstimatedParameter p) {
+ if (p == p1) {
+ return 3.0;
+ } else if (p == p2) {
+ return -1.0;
+ } else {
+ return 0.0;
+ }
+ }
+
+ private static class MyMeasurement
+ extends WeightedMeasurement {
+
+ public MyMeasurement(double weight, double measuredValue,
+ WeightedMeasurementTest testInstance) {
+ super(weight, measuredValue);
+ this.testInstance = testInstance;
+ }
+
+ @Override
+ public double getTheoreticalValue() {
+ return testInstance.theoretical();
+ }
+
+ @Override
+ public double getPartial(EstimatedParameter p) {
+ return testInstance.partial(p);
+ }
+
+ private transient WeightedMeasurementTest testInstance;
+
+ private static final long serialVersionUID = -246712922500792332L;
+
+ }
+
+ private EstimatedParameter p1;
+ private EstimatedParameter p2;
+
+}
diff --git a/src/test/java/org/apache/commons/math/exception/DimensionMismatchExceptionTest.java b/src/test/java/org/apache/commons/math/exception/DimensionMismatchExceptionTest.java
new file mode 100644
index 0000000..5ba51f9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/DimensionMismatchExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link DimensionMismatchException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class DimensionMismatchExceptionTest {
+ @Test
+ public void testAccessors() {
+ final DimensionMismatchException e = new DimensionMismatchException(1, 2);
+ Assert.assertEquals(1, e.getArgument());
+ Assert.assertEquals(2, e.getDimension());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/NonMonotonousSequenceExceptionTest.java b/src/test/java/org/apache/commons/math/exception/NonMonotonousSequenceExceptionTest.java
new file mode 100644
index 0000000..374c4bf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/NonMonotonousSequenceExceptionTest.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.util.MathUtils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link NonMonotonousSequenceException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class NonMonotonousSequenceExceptionTest {
+ @Test
+ public void testAccessors() {
+ NonMonotonousSequenceException e
+ = new NonMonotonousSequenceException(0, -1, 1, MathUtils.OrderDirection.DECREASING, false);
+ Assert.assertEquals(0, e.getArgument());
+ Assert.assertEquals(-1, e.getPrevious());
+ Assert.assertEquals(1, e.getIndex());
+ Assert.assertTrue(e.getDirection() == MathUtils.OrderDirection.DECREASING);
+ Assert.assertFalse(e.getStrict());
+
+ e = new NonMonotonousSequenceException(-1, 0, 1);
+ Assert.assertEquals(-1, e.getArgument());
+ Assert.assertEquals(0, e.getPrevious());
+ Assert.assertEquals(1, e.getIndex());
+ Assert.assertTrue(e.getDirection() == MathUtils.OrderDirection.INCREASING);
+ Assert.assertTrue(e.getStrict());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/NotPositiveExceptionTest.java b/src/test/java/org/apache/commons/math/exception/NotPositiveExceptionTest.java
new file mode 100644
index 0000000..f8a7373
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/NotPositiveExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link NotPositiveException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class NotPositiveExceptionTest {
+ @Test
+ public void testAccessors() {
+ final NotPositiveException e = new NotPositiveException(-1);
+ Assert.assertEquals(-1, e.getArgument());
+ Assert.assertEquals(0, e.getMin());
+ Assert.assertTrue(e.getBoundIsAllowed());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/NotStrictlyPositiveExceptionTest.java b/src/test/java/org/apache/commons/math/exception/NotStrictlyPositiveExceptionTest.java
new file mode 100644
index 0000000..9f48e4d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/NotStrictlyPositiveExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link NotStrictlyPositiveException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class NotStrictlyPositiveExceptionTest {
+ @Test
+ public void testAccessors() {
+ final NotStrictlyPositiveException e = new NotStrictlyPositiveException(0);
+ Assert.assertEquals(0, e.getArgument());
+ Assert.assertEquals(0, e.getMin());
+ Assert.assertFalse(e.getBoundIsAllowed());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/NumberIsTooLargeExceptionTest.java b/src/test/java/org/apache/commons/math/exception/NumberIsTooLargeExceptionTest.java
new file mode 100644
index 0000000..f199fca
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/NumberIsTooLargeExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link NumberIsTooLargeException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class NumberIsTooLargeExceptionTest {
+ @Test
+ public void testAccessors() {
+ final NumberIsTooLargeException e = new NumberIsTooLargeException(1, 0, true);
+ Assert.assertEquals(1, e.getArgument());
+ Assert.assertEquals(0, e.getMax());
+ Assert.assertTrue(e.getBoundIsAllowed());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/NumberIsTooSmallExceptionTest.java b/src/test/java/org/apache/commons/math/exception/NumberIsTooSmallExceptionTest.java
new file mode 100644
index 0000000..95c6575
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/NumberIsTooSmallExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link NumberIsTooSmallException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class NumberIsTooSmallExceptionTest {
+ @Test
+ public void testAccessors() {
+ final NumberIsTooSmallException e = new NumberIsTooSmallException(0, 0, false);
+ Assert.assertEquals(0, e.getArgument());
+ Assert.assertEquals(0, e.getMin());
+ Assert.assertFalse(e.getBoundIsAllowed());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/OutOfRangeExceptionTest.java b/src/test/java/org/apache/commons/math/exception/OutOfRangeExceptionTest.java
new file mode 100644
index 0000000..5d6153c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/OutOfRangeExceptionTest.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.exception;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link OutOfRangeException}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class OutOfRangeExceptionTest {
+ @Test
+ public void testAccessors() {
+ final OutOfRangeException e = new OutOfRangeException(-1, 0, 2);
+ Assert.assertEquals(-1, e.getArgument());
+ Assert.assertEquals(0, e.getLo());
+ Assert.assertEquals(2, e.getHi());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/util/ArgUtilsTest.java b/src/test/java/org/apache/commons/math/exception/util/ArgUtilsTest.java
new file mode 100644
index 0000000..34c4d85
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/util/ArgUtilsTest.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.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link ArgUtils}.
+ *
+ * @version $Revision$ $Date$
+ */
+public class ArgUtilsTest {
+ @Test
+ public void testFlatten() {
+ final List<Object> orig = new ArrayList<Object>();
+
+ final Object[] struct = new Object[] {
+ new Object[] {
+ new Object[] {
+ create(orig),
+ create(orig),
+ },
+ create(orig),
+ new Object[] {
+ create(orig),
+ }
+ },
+ create(orig),
+ new Object[] {
+ create(orig),
+ new Object[] {
+ create(orig),
+ create(orig),
+ }
+ },
+ create(orig),
+ };
+
+ Object[] flat = ArgUtils.flatten(struct);
+ Assert.assertEquals(flat.length, orig.size());
+
+ for (int i = 0, max = orig.size(); i < max; i++) {
+ Assert.assertEquals(orig.get(i), flat[i]);
+ }
+ }
+
+ /**
+ * Create and store an {@code Object}.
+ *
+ * @param list List to store to.
+ * @return the stored object.
+ */
+ private Object create(List<Object> list) {
+ final Object o = new Object();
+ list.add(o);
+ return o;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/exception/util/MessageFactoryTest.java b/src/test/java/org/apache/commons/math/exception/util/MessageFactoryTest.java
new file mode 100644
index 0000000..d0d147c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/exception/util/MessageFactoryTest.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.exception.util;
+
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MessageFactoryTest {
+
+ @Test
+ public void testSpecificGeneral() {
+ Localizable specific = new DummyLocalizable("specific {0} - {1} - {2}");
+ Localizable general = new DummyLocalizable("general {0} / {1}");
+ String message = MessageFactory.buildMessage(Locale.FRENCH, specific, general,
+ 0, 1, 2, 'a', 'b');
+ Assert.assertEquals("general 0 / 1: specific 0 - 1 - 2", message);
+ }
+
+ @Test
+ public void testNullSpecific() {
+ Localizable general = new DummyLocalizable("general {0} / {1}");
+ String message = MessageFactory.buildMessage(Locale.FRENCH, null, general,
+ 0, 1, 2, 'a', 'b');
+ Assert.assertEquals("general 0 / 1", message);
+ }
+
+ @Test
+ public void testNullGeneral() {
+ Localizable specific = new DummyLocalizable("specific {0} - {1} - {2}");
+ String message = MessageFactory.buildMessage(Locale.FRENCH, specific, null,
+ 0, 1, 2, 'a', 'b');
+ Assert.assertEquals("specific 0 - 1 - 2", message);
+ }
+
+ @Test
+ public void testNull() {
+ String message = MessageFactory.buildMessage(Locale.FRENCH, null, null, "nothing");
+ Assert.assertEquals("", message);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/BigFractionFieldTest.java b/src/test/java/org/apache/commons/math/fraction/BigFractionFieldTest.java
new file mode 100644
index 0000000..02c10e8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/BigFractionFieldTest.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.fraction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.TestUtils;
+import org.junit.Test;
+
+public class BigFractionFieldTest {
+
+ @Test
+ public void testZero() {
+ assertEquals(BigFraction.ZERO, BigFractionField.getInstance().getZero());
+ }
+
+ @Test
+ public void testOne() {
+ assertEquals(BigFraction.ONE, BigFractionField.getInstance().getOne());
+ }
+
+ @Test
+ public void testSerial() {
+ // deserializing the singleton should give the singleton itself back
+ BigFractionField field = BigFractionField.getInstance();
+ assertTrue(field == TestUtils.serializeAndRecover(field));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/BigFractionFormatTest.java b/src/test/java/org/apache/commons/math/fraction/BigFractionFormatTest.java
new file mode 100644
index 0000000..391b758
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/BigFractionFormatTest.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.BigDecimal;
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public class BigFractionFormatTest extends TestCase {
+
+ BigFractionFormat properFormat = null;
+ BigFractionFormat improperFormat = null;
+
+ protected Locale getLocale() {
+ return Locale.getDefault();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ properFormat = BigFractionFormat.getProperInstance(getLocale());
+ improperFormat = BigFractionFormat.getImproperInstance(getLocale());
+ }
+
+ public void testFormat() {
+ BigFraction c = new BigFraction(1, 2);
+ String expected = "1 / 2";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatNegative() {
+ BigFraction c = new BigFraction(-1, 2);
+ String expected = "-1 / 2";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatZero() {
+ BigFraction c = new BigFraction(0, 1);
+ String expected = "0 / 1";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatImproper() {
+ BigFraction c = new BigFraction(5, 3);
+
+ String actual = properFormat.format(c);
+ assertEquals("1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ assertEquals("5 / 3", actual);
+ }
+
+ public void testFormatImproperNegative() {
+ BigFraction c = new BigFraction(-5, 3);
+
+ String actual = properFormat.format(c);
+ assertEquals("-1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ assertEquals("-5 / 3", actual);
+ }
+
+ public void testParse() {
+ String source = "1 / 2";
+
+ try {
+ BigFraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(BigInteger.ONE, c.getNumerator());
+ assertEquals(BigInteger.valueOf(2l), c.getDenominator());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(BigInteger.ONE, c.getNumerator());
+ assertEquals(BigInteger.valueOf(2l), c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseInteger() {
+ String source = "10";
+ try {
+ BigFraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(BigInteger.TEN, c.getNumerator());
+ assertEquals(BigInteger.ONE, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ try {
+ BigFraction c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(BigInteger.TEN, c.getNumerator());
+ assertEquals(BigInteger.ONE, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseInvalid() {
+ String source = "a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseInvalidDenominator() {
+ String source = "10 / a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseNegative() {
+
+ try {
+ String source = "-1 / 2";
+ BigFraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumeratorAsInt());
+ assertEquals(2, c.getDenominatorAsInt());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumeratorAsInt());
+ assertEquals(2, c.getDenominatorAsInt());
+
+ source = "1 / -2";
+ c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumeratorAsInt());
+ assertEquals(2, c.getDenominatorAsInt());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumeratorAsInt());
+ assertEquals(2, c.getDenominatorAsInt());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseProper() {
+ String source = "1 2 / 3";
+
+ try {
+ BigFraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(5, c.getNumeratorAsInt());
+ assertEquals(3, c.getDenominatorAsInt());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+
+ try {
+ improperFormat.parse(source);
+ fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseProperNegative() {
+ String source = "-1 2 / 3";
+ try {
+ BigFraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-5, c.getNumeratorAsInt());
+ assertEquals(3, c.getDenominatorAsInt());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+
+ try {
+ improperFormat.parse(source);
+ fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseProperInvalidMinus() {
+ String source = "2 -2 / 3";
+ try {
+ properFormat.parse(source);
+ fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ source = "2 2 / -3";
+ try {
+ properFormat.parse(source);
+ fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ }
+
+ public void testParseBig() throws ParseException {
+ BigFraction f1 =
+ improperFormat.parse("167213075789791382630275400487886041651764456874403" +
+ " / " +
+ "53225575123090058458126718248444563466137046489291");
+ assertEquals(FastMath.PI, f1.doubleValue(), 0.0);
+ BigFraction f2 =
+ properFormat.parse("3 " +
+ "7536350420521207255895245742552351253353317406530" +
+ " / " +
+ "53225575123090058458126718248444563466137046489291");
+ assertEquals(FastMath.PI, f2.doubleValue(), 0.0);
+ assertEquals(f1, f2);
+ BigDecimal pi =
+ new BigDecimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068");
+ assertEquals(pi, f1.bigDecimalValue(99, BigDecimal.ROUND_HALF_EVEN));
+ }
+
+ public void testNumeratorFormat() {
+ NumberFormat old = properFormat.getNumeratorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setNumeratorFormat(nf);
+ assertEquals(nf, properFormat.getNumeratorFormat());
+ properFormat.setNumeratorFormat(old);
+
+ old = improperFormat.getNumeratorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setNumeratorFormat(nf);
+ assertEquals(nf, improperFormat.getNumeratorFormat());
+ improperFormat.setNumeratorFormat(old);
+ }
+
+ public void testDenominatorFormat() {
+ NumberFormat old = properFormat.getDenominatorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setDenominatorFormat(nf);
+ assertEquals(nf, properFormat.getDenominatorFormat());
+ properFormat.setDenominatorFormat(old);
+
+ old = improperFormat.getDenominatorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setDenominatorFormat(nf);
+ assertEquals(nf, improperFormat.getDenominatorFormat());
+ improperFormat.setDenominatorFormat(old);
+ }
+
+ public void testWholeFormat() {
+ ProperBigFractionFormat format = (ProperBigFractionFormat)properFormat;
+
+ NumberFormat old = format.getWholeFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ format.setWholeFormat(nf);
+ assertEquals(nf, format.getWholeFormat());
+ format.setWholeFormat(old);
+ }
+
+ public void testLongFormat() {
+ assertEquals("10 / 1", improperFormat.format(10l));
+ }
+
+ public void testDoubleFormat() {
+ assertEquals("1 / 16", improperFormat.format(0.0625));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/BigFractionTest.java b/src/test/java/org/apache/commons/math/fraction/BigFractionTest.java
new file mode 100644
index 0000000..70096e2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/BigFractionTest.java
@@ -0,0 +1,577 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.fraction;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public class BigFractionTest extends TestCase {
+
+ private void assertFraction(int expectedNumerator, int expectedDenominator, BigFraction actual) {
+ assertEquals(expectedNumerator, actual.getNumeratorAsInt());
+ assertEquals(expectedDenominator, actual.getDenominatorAsInt());
+ }
+
+ private void assertFraction(long expectedNumerator, long expectedDenominator, BigFraction actual) {
+ assertEquals(expectedNumerator, actual.getNumeratorAsLong());
+ assertEquals(expectedDenominator, actual.getDenominatorAsLong());
+ }
+
+ public void testConstructor() {
+ assertFraction(0, 1, new BigFraction(0, 1));
+ assertFraction(0, 1, new BigFraction(0l, 2l));
+ assertFraction(0, 1, new BigFraction(0, -1));
+ assertFraction(1, 2, new BigFraction(1, 2));
+ assertFraction(1, 2, new BigFraction(2, 4));
+ assertFraction(-1, 2, new BigFraction(-1, 2));
+ assertFraction(-1, 2, new BigFraction(1, -2));
+ assertFraction(-1, 2, new BigFraction(-2, 4));
+ assertFraction(-1, 2, new BigFraction(2, -4));
+ assertFraction(11, 1, new BigFraction(11));
+ assertFraction(11, 1, new BigFraction(11l));
+ assertFraction(11, 1, new BigFraction(new BigInteger("11")));
+
+ try {
+ assertFraction(0, 1, new BigFraction(0.00000000000001, 1.0e-5, 100));
+ assertFraction(2, 5, new BigFraction(0.40000000000001, 1.0e-5, 100));
+ assertFraction(15, 1, new BigFraction(15.0000000000001, 1.0e-5, 100));
+ } catch (ConvergenceException ex) {
+ fail(ex.getMessage());
+ }
+ assertEquals(0.00000000000001, new BigFraction(0.00000000000001).doubleValue(), 0.0);
+ assertEquals(0.40000000000001, new BigFraction(0.40000000000001).doubleValue(), 0.0);
+ assertEquals(15.0000000000001, new BigFraction(15.0000000000001).doubleValue(), 0.0);
+ assertFraction(3602879701896487l, 9007199254740992l, new BigFraction(0.40000000000001));
+ assertFraction(1055531162664967l, 70368744177664l, new BigFraction(15.0000000000001));
+ try {
+ new BigFraction(null, BigInteger.ONE);
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ try {
+ new BigFraction(BigInteger.ONE, null);
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ try {
+ new BigFraction(BigInteger.ONE, BigInteger.ZERO);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException npe) {
+ // expected
+ }
+ try {
+ new BigFraction(2.0 * Integer.MAX_VALUE, 1.0e-5, 100000);
+ fail("Expecting FractionConversionException");
+ } catch (FractionConversionException fce) {
+ // expected
+ }
+ }
+
+ public void testGoldenRatio() {
+ try {
+ // the golden ratio is notoriously a difficult number for continuous
+ // fraction
+ new BigFraction((1 + FastMath.sqrt(5)) / 2, 1.0e-12, 25);
+ fail("an exception should have been thrown");
+ } catch (ConvergenceException ce) {
+ // expected behavior
+ }
+ }
+
+ // MATH-179
+ public void testDoubleConstructor() throws ConvergenceException {
+ assertFraction(1, 2, new BigFraction((double) 1 / (double) 2, 1.0e-5, 100));
+ assertFraction(1, 3, new BigFraction((double) 1 / (double) 3, 1.0e-5, 100));
+ assertFraction(2, 3, new BigFraction((double) 2 / (double) 3, 1.0e-5, 100));
+ assertFraction(1, 4, new BigFraction((double) 1 / (double) 4, 1.0e-5, 100));
+ assertFraction(3, 4, new BigFraction((double) 3 / (double) 4, 1.0e-5, 100));
+ assertFraction(1, 5, new BigFraction((double) 1 / (double) 5, 1.0e-5, 100));
+ assertFraction(2, 5, new BigFraction((double) 2 / (double) 5, 1.0e-5, 100));
+ assertFraction(3, 5, new BigFraction((double) 3 / (double) 5, 1.0e-5, 100));
+ assertFraction(4, 5, new BigFraction((double) 4 / (double) 5, 1.0e-5, 100));
+ assertFraction(1, 6, new BigFraction((double) 1 / (double) 6, 1.0e-5, 100));
+ assertFraction(5, 6, new BigFraction((double) 5 / (double) 6, 1.0e-5, 100));
+ assertFraction(1, 7, new BigFraction((double) 1 / (double) 7, 1.0e-5, 100));
+ assertFraction(2, 7, new BigFraction((double) 2 / (double) 7, 1.0e-5, 100));
+ assertFraction(3, 7, new BigFraction((double) 3 / (double) 7, 1.0e-5, 100));
+ assertFraction(4, 7, new BigFraction((double) 4 / (double) 7, 1.0e-5, 100));
+ assertFraction(5, 7, new BigFraction((double) 5 / (double) 7, 1.0e-5, 100));
+ assertFraction(6, 7, new BigFraction((double) 6 / (double) 7, 1.0e-5, 100));
+ assertFraction(1, 8, new BigFraction((double) 1 / (double) 8, 1.0e-5, 100));
+ assertFraction(3, 8, new BigFraction((double) 3 / (double) 8, 1.0e-5, 100));
+ assertFraction(5, 8, new BigFraction((double) 5 / (double) 8, 1.0e-5, 100));
+ assertFraction(7, 8, new BigFraction((double) 7 / (double) 8, 1.0e-5, 100));
+ assertFraction(1, 9, new BigFraction((double) 1 / (double) 9, 1.0e-5, 100));
+ assertFraction(2, 9, new BigFraction((double) 2 / (double) 9, 1.0e-5, 100));
+ assertFraction(4, 9, new BigFraction((double) 4 / (double) 9, 1.0e-5, 100));
+ assertFraction(5, 9, new BigFraction((double) 5 / (double) 9, 1.0e-5, 100));
+ assertFraction(7, 9, new BigFraction((double) 7 / (double) 9, 1.0e-5, 100));
+ assertFraction(8, 9, new BigFraction((double) 8 / (double) 9, 1.0e-5, 100));
+ assertFraction(1, 10, new BigFraction((double) 1 / (double) 10, 1.0e-5, 100));
+ assertFraction(3, 10, new BigFraction((double) 3 / (double) 10, 1.0e-5, 100));
+ assertFraction(7, 10, new BigFraction((double) 7 / (double) 10, 1.0e-5, 100));
+ assertFraction(9, 10, new BigFraction((double) 9 / (double) 10, 1.0e-5, 100));
+ assertFraction(1, 11, new BigFraction((double) 1 / (double) 11, 1.0e-5, 100));
+ assertFraction(2, 11, new BigFraction((double) 2 / (double) 11, 1.0e-5, 100));
+ assertFraction(3, 11, new BigFraction((double) 3 / (double) 11, 1.0e-5, 100));
+ assertFraction(4, 11, new BigFraction((double) 4 / (double) 11, 1.0e-5, 100));
+ assertFraction(5, 11, new BigFraction((double) 5 / (double) 11, 1.0e-5, 100));
+ assertFraction(6, 11, new BigFraction((double) 6 / (double) 11, 1.0e-5, 100));
+ assertFraction(7, 11, new BigFraction((double) 7 / (double) 11, 1.0e-5, 100));
+ assertFraction(8, 11, new BigFraction((double) 8 / (double) 11, 1.0e-5, 100));
+ assertFraction(9, 11, new BigFraction((double) 9 / (double) 11, 1.0e-5, 100));
+ assertFraction(10, 11, new BigFraction((double) 10 / (double) 11, 1.0e-5, 100));
+ }
+
+ // MATH-181
+ public void testDigitLimitConstructor() throws ConvergenceException {
+ assertFraction(2, 5, new BigFraction(0.4, 9));
+ assertFraction(2, 5, new BigFraction(0.4, 99));
+ assertFraction(2, 5, new BigFraction(0.4, 999));
+
+ assertFraction(3, 5, new BigFraction(0.6152, 9));
+ assertFraction(8, 13, new BigFraction(0.6152, 99));
+ assertFraction(510, 829, new BigFraction(0.6152, 999));
+ assertFraction(769, 1250, new BigFraction(0.6152, 9999));
+ }
+
+ public void testEpsilonLimitConstructor() throws ConvergenceException {
+ assertFraction(2, 5, new BigFraction(0.4, 1.0e-5, 100));
+
+ assertFraction(3, 5, new BigFraction(0.6152, 0.02, 100));
+ assertFraction(8, 13, new BigFraction(0.6152, 1.0e-3, 100));
+ assertFraction(251, 408, new BigFraction(0.6152, 1.0e-4, 100));
+ assertFraction(251, 408, new BigFraction(0.6152, 1.0e-5, 100));
+ assertFraction(510, 829, new BigFraction(0.6152, 1.0e-6, 100));
+ assertFraction(769, 1250, new BigFraction(0.6152, 1.0e-7, 100));
+ }
+
+ public void testCompareTo() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+ BigFraction third = new BigFraction(1, 2);
+
+ assertEquals(0, first.compareTo(first));
+ assertEquals(0, first.compareTo(third));
+ assertEquals(1, first.compareTo(second));
+ assertEquals(-1, second.compareTo(first));
+
+ // these two values are different approximations of PI
+ // the first one is approximately PI - 3.07e-18
+ // the second one is approximately PI + 1.936e-17
+ BigFraction pi1 = new BigFraction(1068966896, 340262731);
+ BigFraction pi2 = new BigFraction( 411557987, 131002976);
+ assertEquals(-1, pi1.compareTo(pi2));
+ assertEquals( 1, pi2.compareTo(pi1));
+ assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
+
+ }
+
+ public void testDoubleValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+
+ assertEquals(0.5, first.doubleValue(), 0.0);
+ assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
+ }
+
+ public void testFloatValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+
+ assertEquals(0.5f, first.floatValue(), 0.0f);
+ assertEquals((float) (1.0 / 3.0), second.floatValue(), 0.0f);
+ }
+
+ public void testIntValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(3, 2);
+
+ assertEquals(0, first.intValue());
+ assertEquals(1, second.intValue());
+ }
+
+ public void testLongValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(3, 2);
+
+ assertEquals(0L, first.longValue());
+ assertEquals(1L, second.longValue());
+ }
+
+ public void testConstructorDouble() {
+ assertFraction(1, 2, new BigFraction(0.5));
+ assertFraction(6004799503160661l, 18014398509481984l, new BigFraction(1.0 / 3.0));
+ assertFraction(6124895493223875l, 36028797018963968l, new BigFraction(17.0 / 100.0));
+ assertFraction(1784551352345559l, 562949953421312l, new BigFraction(317.0 / 100.0));
+ assertFraction(-1, 2, new BigFraction(-0.5));
+ assertFraction(-6004799503160661l, 18014398509481984l, new BigFraction(-1.0 / 3.0));
+ assertFraction(-6124895493223875l, 36028797018963968l, new BigFraction(17.0 / -100.0));
+ assertFraction(-1784551352345559l, 562949953421312l, new BigFraction(-317.0 / 100.0));
+ for (double v : new double[] { Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY}) {
+ try {
+ new BigFraction(v);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+ assertEquals(1l, new BigFraction(Double.MAX_VALUE).getDenominatorAsLong());
+ assertEquals(1l, new BigFraction(Double.longBitsToDouble(0x0010000000000000L)).getNumeratorAsLong());
+ assertEquals(1l, new BigFraction(Double.MIN_VALUE).getNumeratorAsLong());
+ }
+
+ public void testAbs() {
+ BigFraction a = new BigFraction(10, 21);
+ BigFraction b = new BigFraction(-10, 21);
+ BigFraction c = new BigFraction(10, -21);
+
+ assertFraction(10, 21, a.abs());
+ assertFraction(10, 21, b.abs());
+ assertFraction(10, 21, c.abs());
+ }
+
+ public void testReciprocal() {
+ BigFraction f = null;
+
+ f = new BigFraction(50, 75);
+ f = f.reciprocal();
+ assertEquals(3, f.getNumeratorAsInt());
+ assertEquals(2, f.getDenominatorAsInt());
+
+ f = new BigFraction(4, 3);
+ f = f.reciprocal();
+ assertEquals(3, f.getNumeratorAsInt());
+ assertEquals(4, f.getDenominatorAsInt());
+
+ f = new BigFraction(-15, 47);
+ f = f.reciprocal();
+ assertEquals(-47, f.getNumeratorAsInt());
+ assertEquals(15, f.getDenominatorAsInt());
+
+ f = new BigFraction(0, 3);
+ try {
+ f = f.reciprocal();
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+
+ // large values
+ f = new BigFraction(Integer.MAX_VALUE, 1);
+ f = f.reciprocal();
+ assertEquals(1, f.getNumeratorAsInt());
+ assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ }
+
+ public void testNegate() {
+ BigFraction f = null;
+
+ f = new BigFraction(50, 75);
+ f = f.negate();
+ assertEquals(-2, f.getNumeratorAsInt());
+ assertEquals(3, f.getDenominatorAsInt());
+
+ f = new BigFraction(-50, 75);
+ f = f.negate();
+ assertEquals(2, f.getNumeratorAsInt());
+ assertEquals(3, f.getDenominatorAsInt());
+
+ // large values
+ f = new BigFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+ f = f.negate();
+ assertEquals(Integer.MIN_VALUE + 2, f.getNumeratorAsInt());
+ assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+
+ }
+
+ public void testAdd() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 1, a.add(a));
+ assertFraction(7, 6, a.add(b));
+ assertFraction(7, 6, b.add(a));
+ assertFraction(4, 3, b.add(b));
+
+ BigFraction f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ BigFraction f2 = BigFraction.ONE;
+ BigFraction f = f1.add(f2);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(-1, 13 * 13 * 2 * 2);
+ f2 = new BigFraction(-2, 13 * 17 * 2);
+ f = f1.add(f2);
+ assertEquals(13 * 13 * 17 * 2 * 2, f.getDenominatorAsInt());
+ assertEquals(-17 - 2 * 13 * 2, f.getNumeratorAsInt());
+
+ try {
+ f.add((BigFraction) null);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ // if this fraction is added naively, it will overflow.
+ // check that it doesn't.
+ f1 = new BigFraction(1, 32768 * 3);
+ f2 = new BigFraction(1, 59049);
+ f = f1.add(f2);
+ assertEquals(52451, f.getNumeratorAsInt());
+ assertEquals(1934917632, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, 3);
+ f2 = new BigFraction(1, 3);
+ f = f1.add(f2);
+ assertEquals(Integer.MIN_VALUE + 1, f.getNumeratorAsInt());
+ assertEquals(3, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(BigInteger.ONE);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(BigInteger.ZERO);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(1);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(0);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(1l);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(0l);
+ assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ }
+
+ public void testDivide() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 1, a.divide(a));
+ assertFraction(3, 4, a.divide(b));
+ assertFraction(4, 3, b.divide(a));
+ assertFraction(1, 1, b.divide(b));
+
+ BigFraction f1 = new BigFraction(3, 5);
+ BigFraction f2 = BigFraction.ZERO;
+ try {
+ f1.divide(f2);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+
+ f1 = new BigFraction(0, 5);
+ f2 = new BigFraction(2, 7);
+ BigFraction f = f1.divide(f2);
+ assertSame(BigFraction.ZERO, f);
+
+ f1 = new BigFraction(2, 7);
+ f2 = BigFraction.ONE;
+ f = f1.divide(f2);
+ assertEquals(2, f.getNumeratorAsInt());
+ assertEquals(7, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1);
+ assertEquals(1, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f2 = new BigFraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f2);
+ assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ try {
+ f.divide((BigFraction) null);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide(BigInteger.valueOf(Integer.MIN_VALUE));
+ assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ assertEquals(1, f.getNumeratorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide(Integer.MIN_VALUE);
+ assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ assertEquals(1, f.getNumeratorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide((long) Integer.MIN_VALUE);
+ assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ assertEquals(1, f.getNumeratorAsInt());
+
+ }
+
+ public void testMultiply() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 4, a.multiply(a));
+ assertFraction(1, 3, a.multiply(b));
+ assertFraction(1, 3, b.multiply(a));
+ assertFraction(4, 9, b.multiply(b));
+
+ BigFraction f1 = new BigFraction(Integer.MAX_VALUE, 1);
+ BigFraction f2 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ BigFraction f = f1.multiply(f2);
+ assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f = f2.multiply(Integer.MAX_VALUE);
+ assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ f = f2.multiply((long) Integer.MAX_VALUE);
+ assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ try {
+ f.multiply((BigFraction) null);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ }
+
+ public void testSubtract() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(0, 1, a.subtract(a));
+ assertFraction(-1, 6, a.subtract(b));
+ assertFraction(1, 6, b.subtract(a));
+ assertFraction(0, 1, b.subtract(b));
+
+ BigFraction f = new BigFraction(1, 1);
+ try {
+ f.subtract((BigFraction) null);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ // if this fraction is subtracted naively, it will overflow.
+ // check that it doesn't.
+ BigFraction f1 = new BigFraction(1, 32768 * 3);
+ BigFraction f2 = new BigFraction(1, 59049);
+ f = f1.subtract(f2);
+ assertEquals(-13085, f.getNumeratorAsInt());
+ assertEquals(1934917632, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, 3);
+ f2 = new BigFraction(1, 3).negate();
+ f = f1.subtract(f2);
+ assertEquals(Integer.MIN_VALUE + 1, f.getNumeratorAsInt());
+ assertEquals(3, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE, 1);
+ f2 = BigFraction.ONE;
+ f = f1.subtract(f2);
+ assertEquals(Integer.MAX_VALUE - 1, f.getNumeratorAsInt());
+ assertEquals(1, f.getDenominatorAsInt());
+
+ }
+
+ public void testBigDecimalValue() {
+ assertEquals(new BigDecimal(0.5), new BigFraction(1, 2).bigDecimalValue());
+ assertEquals(new BigDecimal("0.0003"), new BigFraction(3, 10000).bigDecimalValue());
+ assertEquals(new BigDecimal("0"), new BigFraction(1, 3).bigDecimalValue(BigDecimal.ROUND_DOWN));
+ assertEquals(new BigDecimal("0.333"), new BigFraction(1, 3).bigDecimalValue(3, BigDecimal.ROUND_DOWN));
+ }
+
+ public void testEqualsAndHashCode() {
+ BigFraction zero = new BigFraction(0, 1);
+ BigFraction nullFraction = null;
+ assertTrue(zero.equals(zero));
+ assertFalse(zero.equals(nullFraction));
+ assertFalse(zero.equals(Double.valueOf(0)));
+ BigFraction zero2 = new BigFraction(0, 2);
+ assertTrue(zero.equals(zero2));
+ assertEquals(zero.hashCode(), zero2.hashCode());
+ BigFraction one = new BigFraction(1, 1);
+ assertFalse((one.equals(zero) || zero.equals(one)));
+ assertTrue(one.equals(BigFraction.ONE));
+ }
+
+ public void testGetReducedFraction() {
+ BigFraction threeFourths = new BigFraction(3, 4);
+ assertTrue(threeFourths.equals(BigFraction.getReducedFraction(6, 8)));
+ assertTrue(BigFraction.ZERO.equals(BigFraction.getReducedFraction(0, -1)));
+ try {
+ BigFraction.getReducedFraction(1, 0);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected
+ }
+ assertEquals(BigFraction.getReducedFraction(2, Integer.MIN_VALUE).getNumeratorAsInt(), -1);
+ assertEquals(BigFraction.getReducedFraction(1, -1).getNumeratorAsInt(), -1);
+ }
+
+ public void testPow() {
+ assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(13));
+ assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(13l));
+ assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(BigInteger.valueOf(13l)));
+ assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(0));
+ assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(0l));
+ assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(BigInteger.valueOf(0l)));
+ assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(-13));
+ assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(-13l));
+ assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(BigInteger.valueOf(-13l)));
+ }
+
+ public void testMath340() {
+ BigFraction fractionA = new BigFraction(0.00131);
+ BigFraction fractionB = new BigFraction(.37).reciprocal();
+ BigFraction errorResult = fractionA.multiply(fractionB);
+ BigFraction correctResult = new BigFraction(fractionA.getNumerator().multiply(fractionB.getNumerator()),
+ fractionA.getDenominator().multiply(fractionB.getDenominator()));
+ assertEquals(correctResult, errorResult);
+ }
+
+ public void testSerial() throws FractionConversionException {
+ BigFraction[] fractions = {
+ new BigFraction(3, 4), BigFraction.ONE, BigFraction.ZERO,
+ new BigFraction(17), new BigFraction(FastMath.PI, 1000),
+ new BigFraction(-5, 2)
+ };
+ for (BigFraction fraction : fractions) {
+ assertEquals(fraction, TestUtils.serializeAndRecover(fraction));
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/FractionFieldTest.java b/src/test/java/org/apache/commons/math/fraction/FractionFieldTest.java
new file mode 100644
index 0000000..37ece32
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/FractionFieldTest.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.fraction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.TestUtils;
+import org.junit.Test;
+
+public class FractionFieldTest {
+
+ @Test
+ public void testZero() {
+ assertEquals(Fraction.ZERO, FractionField.getInstance().getZero());
+ }
+
+ @Test
+ public void testOne() {
+ assertEquals(Fraction.ONE, FractionField.getInstance().getOne());
+ }
+
+ @Test
+ public void testSerial() {
+ // deserializing the singleton should give the singleton itself back
+ FractionField field = FractionField.getInstance();
+ assertTrue(field == TestUtils.serializeAndRecover(field));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/FractionFormatTest.java b/src/test/java/org/apache/commons/math/fraction/FractionFormatTest.java
new file mode 100644
index 0000000..21f64f5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/FractionFormatTest.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.fraction;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public class FractionFormatTest extends TestCase {
+
+ FractionFormat properFormat = null;
+ FractionFormat improperFormat = null;
+
+ protected Locale getLocale() {
+ return Locale.getDefault();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ properFormat = FractionFormat.getProperInstance(getLocale());
+ improperFormat = FractionFormat.getImproperInstance(getLocale());
+ }
+
+ public void testFormat() {
+ Fraction c = new Fraction(1, 2);
+ String expected = "1 / 2";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatNegative() {
+ Fraction c = new Fraction(-1, 2);
+ String expected = "-1 / 2";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatZero() {
+ Fraction c = new Fraction(0, 1);
+ String expected = "0 / 1";
+
+ String actual = properFormat.format(c);
+ assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testFormatImproper() {
+ Fraction c = new Fraction(5, 3);
+
+ String actual = properFormat.format(c);
+ assertEquals("1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ assertEquals("5 / 3", actual);
+ }
+
+ public void testFormatImproperNegative() {
+ Fraction c = new Fraction(-5, 3);
+
+ String actual = properFormat.format(c);
+ assertEquals("-1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ assertEquals("-5 / 3", actual);
+ }
+
+ public void testParse() {
+ String source = "1 / 2";
+
+ try {
+ Fraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseInteger() {
+ String source = "10";
+ try {
+ Fraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(10, c.getNumerator());
+ assertEquals(1, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ try {
+ Fraction c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(10, c.getNumerator());
+ assertEquals(1, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseInvalid() {
+ String source = "a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseInvalidDenominator() {
+ String source = "10 / a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseNegative() {
+
+ try {
+ String source = "-1 / 2";
+ Fraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+
+ source = "1 / -2";
+ c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-1, c.getNumerator());
+ assertEquals(2, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseProper() {
+ String source = "1 2 / 3";
+
+ try {
+ Fraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(5, c.getNumerator());
+ assertEquals(3, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+
+ try {
+ improperFormat.parse(source);
+ fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseProperNegative() {
+ String source = "-1 2 / 3";
+ try {
+ Fraction c = properFormat.parse(source);
+ assertNotNull(c);
+ assertEquals(-5, c.getNumerator());
+ assertEquals(3, c.getDenominator());
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+
+ try {
+ improperFormat.parse(source);
+ fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ public void testParseProperInvalidMinus() {
+ String source = "2 -2 / 3";
+ try {
+ properFormat.parse(source);
+ fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ source = "2 2 / -3";
+ try {
+ properFormat.parse(source);
+ fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ }
+
+ public void testNumeratorFormat() {
+ NumberFormat old = properFormat.getNumeratorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setNumeratorFormat(nf);
+ assertEquals(nf, properFormat.getNumeratorFormat());
+ properFormat.setNumeratorFormat(old);
+
+ old = improperFormat.getNumeratorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setNumeratorFormat(nf);
+ assertEquals(nf, improperFormat.getNumeratorFormat());
+ improperFormat.setNumeratorFormat(old);
+ }
+
+ public void testDenominatorFormat() {
+ NumberFormat old = properFormat.getDenominatorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setDenominatorFormat(nf);
+ assertEquals(nf, properFormat.getDenominatorFormat());
+ properFormat.setDenominatorFormat(old);
+
+ old = improperFormat.getDenominatorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setDenominatorFormat(nf);
+ assertEquals(nf, improperFormat.getDenominatorFormat());
+ improperFormat.setDenominatorFormat(old);
+ }
+
+ public void testWholeFormat() {
+ ProperFractionFormat format = (ProperFractionFormat)properFormat;
+
+ NumberFormat old = format.getWholeFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ format.setWholeFormat(nf);
+ assertEquals(nf, format.getWholeFormat());
+ format.setWholeFormat(old);
+ }
+
+ public void testLongFormat() {
+ assertEquals("10 / 1", improperFormat.format(10l));
+ }
+
+ public void testDoubleFormat() {
+ assertEquals("355 / 113", improperFormat.format(FastMath.PI));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/fraction/FractionTest.java b/src/test/java/org/apache/commons/math/fraction/FractionTest.java
new file mode 100644
index 0000000..23fe9e2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/fraction/FractionTest.java
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 1003907 $ $Date: 2010-10-03 00:23:34 +0200 (dim. 03 oct. 2010) $
+ */
+public class FractionTest extends TestCase {
+
+ private void assertFraction(int expectedNumerator, int expectedDenominator, Fraction actual) {
+ assertEquals(expectedNumerator, actual.getNumerator());
+ assertEquals(expectedDenominator, actual.getDenominator());
+ }
+
+ public void testConstructor() {
+ assertFraction(0, 1, new Fraction(0, 1));
+ assertFraction(0, 1, new Fraction(0, 2));
+ assertFraction(0, 1, new Fraction(0, -1));
+ assertFraction(1, 2, new Fraction(1, 2));
+ assertFraction(1, 2, new Fraction(2, 4));
+ assertFraction(-1, 2, new Fraction(-1, 2));
+ assertFraction(-1, 2, new Fraction(1, -2));
+ assertFraction(-1, 2, new Fraction(-2, 4));
+ assertFraction(-1, 2, new Fraction(2, -4));
+
+ // overflow
+ try {
+ new Fraction(Integer.MIN_VALUE, -1);
+ fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ try {
+ new Fraction(1, Integer.MIN_VALUE);
+ fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ try {
+ assertFraction(0, 1, new Fraction(0.00000000000001));
+ assertFraction(2, 5, new Fraction(0.40000000000001));
+ assertFraction(15, 1, new Fraction(15.0000000000001));
+
+ } catch (ConvergenceException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testGoldenRatio() {
+ try {
+ // the golden ratio is notoriously a difficult number for continuous fraction
+ new Fraction((1 + FastMath.sqrt(5)) / 2, 1.0e-12, 25);
+ fail("an exception should have been thrown");
+ } catch (ConvergenceException ce) {
+ // expected behavior
+ }
+ }
+
+ // MATH-179
+ public void testDoubleConstructor() throws ConvergenceException {
+ assertFraction(1, 2, new Fraction((double)1 / (double)2));
+ assertFraction(1, 3, new Fraction((double)1 / (double)3));
+ assertFraction(2, 3, new Fraction((double)2 / (double)3));
+ assertFraction(1, 4, new Fraction((double)1 / (double)4));
+ assertFraction(3, 4, new Fraction((double)3 / (double)4));
+ assertFraction(1, 5, new Fraction((double)1 / (double)5));
+ assertFraction(2, 5, new Fraction((double)2 / (double)5));
+ assertFraction(3, 5, new Fraction((double)3 / (double)5));
+ assertFraction(4, 5, new Fraction((double)4 / (double)5));
+ assertFraction(1, 6, new Fraction((double)1 / (double)6));
+ assertFraction(5, 6, new Fraction((double)5 / (double)6));
+ assertFraction(1, 7, new Fraction((double)1 / (double)7));
+ assertFraction(2, 7, new Fraction((double)2 / (double)7));
+ assertFraction(3, 7, new Fraction((double)3 / (double)7));
+ assertFraction(4, 7, new Fraction((double)4 / (double)7));
+ assertFraction(5, 7, new Fraction((double)5 / (double)7));
+ assertFraction(6, 7, new Fraction((double)6 / (double)7));
+ assertFraction(1, 8, new Fraction((double)1 / (double)8));
+ assertFraction(3, 8, new Fraction((double)3 / (double)8));
+ assertFraction(5, 8, new Fraction((double)5 / (double)8));
+ assertFraction(7, 8, new Fraction((double)7 / (double)8));
+ assertFraction(1, 9, new Fraction((double)1 / (double)9));
+ assertFraction(2, 9, new Fraction((double)2 / (double)9));
+ assertFraction(4, 9, new Fraction((double)4 / (double)9));
+ assertFraction(5, 9, new Fraction((double)5 / (double)9));
+ assertFraction(7, 9, new Fraction((double)7 / (double)9));
+ assertFraction(8, 9, new Fraction((double)8 / (double)9));
+ assertFraction(1, 10, new Fraction((double)1 / (double)10));
+ assertFraction(3, 10, new Fraction((double)3 / (double)10));
+ assertFraction(7, 10, new Fraction((double)7 / (double)10));
+ assertFraction(9, 10, new Fraction((double)9 / (double)10));
+ assertFraction(1, 11, new Fraction((double)1 / (double)11));
+ assertFraction(2, 11, new Fraction((double)2 / (double)11));
+ assertFraction(3, 11, new Fraction((double)3 / (double)11));
+ assertFraction(4, 11, new Fraction((double)4 / (double)11));
+ assertFraction(5, 11, new Fraction((double)5 / (double)11));
+ assertFraction(6, 11, new Fraction((double)6 / (double)11));
+ assertFraction(7, 11, new Fraction((double)7 / (double)11));
+ assertFraction(8, 11, new Fraction((double)8 / (double)11));
+ assertFraction(9, 11, new Fraction((double)9 / (double)11));
+ assertFraction(10, 11, new Fraction((double)10 / (double)11));
+ }
+
+ // MATH-181
+ public void testDigitLimitConstructor() throws ConvergenceException {
+ assertFraction(2, 5, new Fraction(0.4, 9));
+ assertFraction(2, 5, new Fraction(0.4, 99));
+ assertFraction(2, 5, new Fraction(0.4, 999));
+
+ assertFraction(3, 5, new Fraction(0.6152, 9));
+ assertFraction(8, 13, new Fraction(0.6152, 99));
+ assertFraction(510, 829, new Fraction(0.6152, 999));
+ assertFraction(769, 1250, new Fraction(0.6152, 9999));
+ }
+
+ public void testIntegerOverflow() {
+ checkIntegerOverflow(0.75000000001455192);
+ checkIntegerOverflow(1.0e10);
+ }
+
+ private void checkIntegerOverflow(double a) {
+ try {
+ new Fraction(a, 1.0e-12, 1000);
+ fail("an exception should have been thrown");
+ } catch (ConvergenceException ce) {
+ // expected behavior
+ }
+ }
+
+ public void testEpsilonLimitConstructor() throws ConvergenceException {
+ assertFraction(2, 5, new Fraction(0.4, 1.0e-5, 100));
+
+ assertFraction(3, 5, new Fraction(0.6152, 0.02, 100));
+ assertFraction(8, 13, new Fraction(0.6152, 1.0e-3, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-4, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-5, 100));
+ assertFraction(510, 829, new Fraction(0.6152, 1.0e-6, 100));
+ assertFraction(769, 1250, new Fraction(0.6152, 1.0e-7, 100));
+ }
+
+ public void testCompareTo() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+ Fraction third = new Fraction(1, 2);
+
+ assertEquals(0, first.compareTo(first));
+ assertEquals(0, first.compareTo(third));
+ assertEquals(1, first.compareTo(second));
+ assertEquals(-1, second.compareTo(first));
+
+ // these two values are different approximations of PI
+ // the first one is approximately PI - 3.07e-18
+ // the second one is approximately PI + 1.936e-17
+ Fraction pi1 = new Fraction(1068966896, 340262731);
+ Fraction pi2 = new Fraction( 411557987, 131002976);
+ assertEquals(-1, pi1.compareTo(pi2));
+ assertEquals( 1, pi2.compareTo(pi1));
+ assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
+ }
+
+ public void testDoubleValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+
+ assertEquals(0.5, first.doubleValue(), 0.0);
+ assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
+ }
+
+ public void testFloatValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+
+ assertEquals(0.5f, first.floatValue(), 0.0f);
+ assertEquals((float)(1.0 / 3.0), second.floatValue(), 0.0f);
+ }
+
+ public void testIntValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(3, 2);
+
+ assertEquals(0, first.intValue());
+ assertEquals(1, second.intValue());
+ }
+
+ public void testLongValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(3, 2);
+
+ assertEquals(0L, first.longValue());
+ assertEquals(1L, second.longValue());
+ }
+
+ public void testConstructorDouble() {
+ try {
+ assertFraction(1, 2, new Fraction(0.5));
+ assertFraction(1, 3, new Fraction(1.0 / 3.0));
+ assertFraction(17, 100, new Fraction(17.0 / 100.0));
+ assertFraction(317, 100, new Fraction(317.0 / 100.0));
+ assertFraction(-1, 2, new Fraction(-0.5));
+ assertFraction(-1, 3, new Fraction(-1.0 / 3.0));
+ assertFraction(-17, 100, new Fraction(17.0 / -100.0));
+ assertFraction(-317, 100, new Fraction(-317.0 / 100.0));
+ } catch (ConvergenceException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testAbs() {
+ Fraction a = new Fraction(10, 21);
+ Fraction b = new Fraction(-10, 21);
+ Fraction c = new Fraction(10, -21);
+
+ assertFraction(10, 21, a.abs());
+ assertFraction(10, 21, b.abs());
+ assertFraction(10, 21, c.abs());
+ }
+
+ public void testReciprocal() {
+ Fraction f = null;
+
+ f = new Fraction(50, 75);
+ f = f.reciprocal();
+ assertEquals(3, f.getNumerator());
+ assertEquals(2, f.getDenominator());
+
+ f = new Fraction(4, 3);
+ f = f.reciprocal();
+ assertEquals(3, f.getNumerator());
+ assertEquals(4, f.getDenominator());
+
+ f = new Fraction(-15, 47);
+ f = f.reciprocal();
+ assertEquals(-47, f.getNumerator());
+ assertEquals(15, f.getDenominator());
+
+ f = new Fraction(0, 3);
+ try {
+ f = f.reciprocal();
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ // large values
+ f = new Fraction(Integer.MAX_VALUE, 1);
+ f = f.reciprocal();
+ assertEquals(1, f.getNumerator());
+ assertEquals(Integer.MAX_VALUE, f.getDenominator());
+ }
+
+ public void testNegate() {
+ Fraction f = null;
+
+ f = new Fraction(50, 75);
+ f = f.negate();
+ assertEquals(-2, f.getNumerator());
+ assertEquals(3, f.getDenominator());
+
+ f = new Fraction(-50, 75);
+ f = f.negate();
+ assertEquals(2, f.getNumerator());
+ assertEquals(3, f.getDenominator());
+
+ // large values
+ f = new Fraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE);
+ f = f.negate();
+ assertEquals(Integer.MIN_VALUE+2, f.getNumerator());
+ assertEquals(Integer.MAX_VALUE, f.getDenominator());
+
+ f = new Fraction(Integer.MIN_VALUE, 1);
+ try {
+ f = f.negate();
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+ }
+
+ public void testAdd() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 1, a.add(a));
+ assertFraction(7, 6, a.add(b));
+ assertFraction(7, 6, b.add(a));
+ assertFraction(4, 3, b.add(b));
+
+ Fraction f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
+ Fraction f2 = Fraction.ONE;
+ Fraction f = f1.add(f2);
+ assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+ f = f1.add(1);
+ assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ f1 = new Fraction(-1, 13*13*2*2);
+ f2 = new Fraction(-2, 13*17*2);
+ f = f1.add(f2);
+ assertEquals(13*13*17*2*2, f.getDenominator());
+ assertEquals(-17 - 2*13*2, f.getNumerator());
+
+ try {
+ f.add(null);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ // if this fraction is added naively, it will overflow.
+ // check that it doesn't.
+ f1 = new Fraction(1,32768*3);
+ f2 = new Fraction(1,59049);
+ f = f1.add(f2);
+ assertEquals(52451, f.getNumerator());
+ assertEquals(1934917632, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, 3);
+ f2 = new Fraction(1,3);
+ f = f1.add(f2);
+ assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
+ assertEquals(3, f.getDenominator());
+
+ f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
+ f2 = Fraction.ONE;
+ f = f1.add(f2);
+ assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ try {
+ f = f.add(Fraction.ONE); // should overflow
+ fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ // denominator should not be a multiple of 2 or 3 to trigger overflow
+ f1 = new Fraction(Integer.MIN_VALUE, 5);
+ f2 = new Fraction(-1,5);
+ try {
+ f = f1.add(f2); // should overflow
+ fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(-Integer.MAX_VALUE, 1);
+ f = f.add(f);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(-Integer.MAX_VALUE, 1);
+ f = f.add(f);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(3,327680);
+ f2 = new Fraction(2,59049);
+ try {
+ f = f1.add(f2); // should overflow
+ fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+ }
+
+ public void testDivide() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 1, a.divide(a));
+ assertFraction(3, 4, a.divide(b));
+ assertFraction(4, 3, b.divide(a));
+ assertFraction(1, 1, b.divide(b));
+
+ Fraction f1 = new Fraction(3, 5);
+ Fraction f2 = Fraction.ZERO;
+ try {
+ f1.divide(f2);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(0, 5);
+ f2 = new Fraction(2, 7);
+ Fraction f = f1.divide(f2);
+ assertSame(Fraction.ZERO, f);
+
+ f1 = new Fraction(2, 7);
+ f2 = Fraction.ONE;
+ f = f1.divide(f2);
+ assertEquals(2, f.getNumerator());
+ assertEquals(7, f.getDenominator());
+
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1);
+ assertEquals(1, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f2 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f2);
+ assertEquals(Integer.MIN_VALUE, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ try {
+ f.divide(null);
+ fail("IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ try {
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1.reciprocal()); // should overflow
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+ try {
+ f1 = new Fraction(1, -Integer.MAX_VALUE);
+ f = f1.divide(f1.reciprocal()); // should overflow
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(6, 35);
+ f = f1.divide(15);
+ assertEquals(2, f.getNumerator());
+ assertEquals(175, f.getDenominator());
+
+ }
+
+ public void testMultiply() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 4, a.multiply(a));
+ assertFraction(1, 3, a.multiply(b));
+ assertFraction(1, 3, b.multiply(a));
+ assertFraction(4, 9, b.multiply(b));
+
+ Fraction f1 = new Fraction(Integer.MAX_VALUE, 1);
+ Fraction f2 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ Fraction f = f1.multiply(f2);
+ assertEquals(Integer.MIN_VALUE, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ try {
+ f.multiply(null);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ f1 = new Fraction(6, 35);
+ f = f1.multiply(15);
+ assertEquals(18, f.getNumerator());
+ assertEquals(7, f.getDenominator());
+ }
+
+ public void testSubtract() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(0, 1, a.subtract(a));
+ assertFraction(-1, 6, a.subtract(b));
+ assertFraction(1, 6, b.subtract(a));
+ assertFraction(0, 1, b.subtract(b));
+
+ Fraction f = new Fraction(1,1);
+ try {
+ f.subtract(null);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ // if this fraction is subtracted naively, it will overflow.
+ // check that it doesn't.
+ Fraction f1 = new Fraction(1,32768*3);
+ Fraction f2 = new Fraction(1,59049);
+ f = f1.subtract(f2);
+ assertEquals(-13085, f.getNumerator());
+ assertEquals(1934917632, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, 3);
+ f2 = new Fraction(1,3).negate();
+ f = f1.subtract(f2);
+ assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
+ assertEquals(3, f.getDenominator());
+
+ f1 = new Fraction(Integer.MAX_VALUE, 1);
+ f2 = Fraction.ONE;
+ f = f1.subtract(f2);
+ assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+ f = f1.subtract(1);
+ assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
+ assertEquals(1, f.getDenominator());
+
+ try {
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f2 = new Fraction(1, Integer.MAX_VALUE - 1);
+ f = f1.subtract(f2);
+ fail("expecting ArithmeticException"); //should overflow
+ } catch (ArithmeticException ex) {}
+
+ // denominator should not be a multiple of 2 or 3 to trigger overflow
+ f1 = new Fraction(Integer.MIN_VALUE, 5);
+ f2 = new Fraction(1,5);
+ try {
+ f = f1.subtract(f2); // should overflow
+ fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(Integer.MIN_VALUE, 1);
+ f = f.subtract(Fraction.ONE);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(Integer.MAX_VALUE, 1);
+ f = f.subtract(Fraction.ONE.negate());
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(3,327680);
+ f2 = new Fraction(2,59049);
+ try {
+ f = f1.subtract(f2); // should overflow
+ fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+ }
+
+ public void testEqualsAndHashCode() {
+ Fraction zero = new Fraction(0,1);
+ Fraction nullFraction = null;
+ assertTrue( zero.equals(zero));
+ assertFalse(zero.equals(nullFraction));
+ assertFalse(zero.equals(Double.valueOf(0)));
+ Fraction zero2 = new Fraction(0,2);
+ assertTrue(zero.equals(zero2));
+ assertEquals(zero.hashCode(), zero2.hashCode());
+ Fraction one = new Fraction(1,1);
+ assertFalse((one.equals(zero) ||zero.equals(one)));
+ }
+
+ public void testGetReducedFraction() {
+ Fraction threeFourths = new Fraction(3, 4);
+ assertTrue(threeFourths.equals(Fraction.getReducedFraction(6, 8)));
+ assertTrue(Fraction.ZERO.equals(Fraction.getReducedFraction(0, -1)));
+ try {
+ Fraction.getReducedFraction(1, 0);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected
+ }
+ assertEquals(Fraction.getReducedFraction
+ (2, Integer.MIN_VALUE).getNumerator(),-1);
+ assertEquals(Fraction.getReducedFraction
+ (1, -1).getNumerator(), -1);
+ }
+
+ public void testToString() {
+ assertEquals("0", new Fraction(0, 3).toString());
+ assertEquals("3", new Fraction(6, 2).toString());
+ assertEquals("2 / 3", new Fraction(18, 27).toString());
+ }
+
+ public void testSerial() throws FractionConversionException {
+ Fraction[] fractions = {
+ new Fraction(3, 4), Fraction.ONE, Fraction.ZERO,
+ new Fraction(17), new Fraction(FastMath.PI, 1000),
+ new Fraction(-5, 2)
+ };
+ for (Fraction fraction : fractions) {
+ assertEquals(fraction, TestUtils.serializeAndRecover(fraction));
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/BinaryChromosomeTest.java b/src/test/java/org/apache/commons/math/genetics/BinaryChromosomeTest.java
new file mode 100644
index 0000000..e084b2c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/BinaryChromosomeTest.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.genetics;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+public class BinaryChromosomeTest {
+
+ @Test
+ public void testInvalidConstructor() {
+ Integer[][] reprs = new Integer[][] {
+ new Integer[] {0,1,0,1,2},
+ new Integer[] {0,1,0,1,-1}
+ };
+
+ for (Integer[] repr : reprs) {
+ try {
+ new DummyBinaryChromosome(repr);
+ fail("Exception not caught");
+ } catch (IllegalArgumentException e) {
+
+ }
+ }
+ }
+
+ @Test
+ public void testRandomConstructor() {
+ for (int i=0; i<20; i++) {
+ new DummyBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(10));
+ }
+ }
+
+ @Test
+ public void testIsSame() {
+ Chromosome c1 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1});
+ Chromosome c2 = new DummyBinaryChromosome(new Integer[] {0,1,1,0,1});
+ Chromosome c3 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1,1});
+ Chromosome c4 = new DummyBinaryChromosome(new Integer[] {1,1,0,1,0,1});
+ Chromosome c5 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,0});
+ Chromosome c6 = new DummyBinaryChromosome(new Integer[] {0,1,0,1,0,1});
+
+ assertFalse(c1.isSame(c2));
+ assertFalse(c1.isSame(c3));
+ assertFalse(c1.isSame(c4));
+ assertFalse(c1.isSame(c5));
+ assertTrue(c1.isSame(c6));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/BinaryMutationTest.java b/src/test/java/org/apache/commons/math/genetics/BinaryMutationTest.java
new file mode 100644
index 0000000..3483db4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/BinaryMutationTest.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 static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class BinaryMutationTest {
+
+ @Test
+ public void testMutate() {
+ BinaryMutation mutation = new BinaryMutation();
+
+ // stochastic testing :)
+ for (int i=0; i<20; i++) {
+ DummyBinaryChromosome original = new DummyBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(10));
+ DummyBinaryChromosome mutated = (DummyBinaryChromosome) mutation.mutate(original);
+
+ // one gene should be different
+ int numDifferent = 0;
+ for (int j=0; j<original.getRepresentation().size(); j++) {
+ if (original.getRepresentation().get(j) != mutated.getRepresentation().get(j))
+ numDifferent++;
+ }
+ assertEquals(1, numDifferent);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/ChromosomeTest.java b/src/test/java/org/apache/commons/math/genetics/ChromosomeTest.java
new file mode 100644
index 0000000..79be0cc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/ChromosomeTest.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;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+public class ChromosomeTest {
+
+ @Test
+ public void testCompareTo() {
+ Chromosome c1 = new Chromosome() {
+ public double fitness() {
+ return 0;
+ }
+ };
+ Chromosome c2 = new Chromosome() {
+ public double fitness() {
+ return 10;
+ }
+ };
+ Chromosome c3 = new Chromosome() {
+ public double fitness() {
+ return 10;
+ }
+ };
+
+ assertTrue(c1.compareTo(c2) < 0);
+ assertTrue(c2.compareTo(c1) > 0);
+ assertEquals(0,c3.compareTo(c2));
+ assertEquals(0,c2.compareTo(c3));
+ }
+
+ private abstract static class DummyChromosome extends Chromosome {
+ private final int repr;
+
+ public DummyChromosome(final int repr) {
+ this.repr = repr;
+ }
+ @Override
+ protected boolean isSame(Chromosome another) {
+ return ((DummyChromosome) another).repr == repr;
+ }
+ }
+
+ @Test
+ public void testFindSameChromosome() {
+ Chromosome c1 = new DummyChromosome(1) {
+ public double fitness() {
+ return 1;
+ }
+ };
+ Chromosome c2 = new DummyChromosome(2) {
+ public double fitness() {
+ return 2;
+ }
+ };
+ Chromosome c3 = new DummyChromosome(3) {
+ public double fitness() {
+ return 3;
+ }
+ };
+ Chromosome c4 = new DummyChromosome(1) {
+ public double fitness() {
+ return 5;
+ }
+ };
+ Chromosome c5 = new DummyChromosome(15) {
+ public double fitness() {
+ return 15;
+ }
+ };
+
+ List<Chromosome> popChr = new ArrayList<Chromosome>();
+ popChr.add(c1);
+ popChr.add(c2);
+ popChr.add(c3);
+ Population pop = new ListPopulation(popChr,3) {
+ public Population nextGeneration() {
+ // not important
+ return null;
+ }
+ };
+
+ assertNull(c5.findSameChromosome(pop));
+ assertEquals(c1, c4.findSameChromosome(pop));
+
+ c4.searchForFitnessUpdate(pop);
+ assertEquals(1, c4.getFitness(),0);
+ }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/genetics/DummyBinaryChromosome.java b/src/test/java/org/apache/commons/math/genetics/DummyBinaryChromosome.java
new file mode 100644
index 0000000..f76cc57
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/DummyBinaryChromosome.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;
+
+/**
+ * Implementation of BinaryChromosome for testing purposes
+ */
+public class DummyBinaryChromosome extends BinaryChromosome {
+
+ public DummyBinaryChromosome(List<Integer> representation) {
+ super(representation);
+ }
+
+ public DummyBinaryChromosome(Integer[] representation) {
+ super(representation);
+ }
+
+ @Override
+ public AbstractListChromosome<Integer> newFixedLengthChromosome(List<Integer> chromosomeRepresentation) {
+ return new DummyBinaryChromosome(chromosomeRepresentation);
+ }
+
+ public double fitness() {
+ // uninteresting
+ return 0;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/DummyRandomKey.java b/src/test/java/org/apache/commons/math/genetics/DummyRandomKey.java
new file mode 100644
index 0000000..4e688aa
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/DummyRandomKey.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;
+
+/**
+ * Implementation of RandomKey for testing purposes
+ */
+public class DummyRandomKey extends RandomKey<String> {
+
+ public DummyRandomKey(List<Double> representation) {
+ super(representation);
+ }
+
+ public DummyRandomKey(Double[] representation) {
+ super(representation);
+ }
+
+ @Override
+ public AbstractListChromosome<Double> newFixedLengthChromosome(List<Double> chromosomeRepresentation) {
+ return new DummyRandomKey(chromosomeRepresentation);
+ }
+
+ public double fitness() {
+ // unimportant
+ return 0;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/ElitisticListPopulationTest.java b/src/test/java/org/apache/commons/math/genetics/ElitisticListPopulationTest.java
new file mode 100644
index 0000000..fad1945
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/ElitisticListPopulationTest.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.genetics;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ElitisticListPopulationTest {
+
+ private static int counter = 0;
+
+ @Test
+ public void testNextGeneration() {
+ ElitisticListPopulation pop = new ElitisticListPopulation(100, 0.203);
+
+ for (int i=0; i<pop.getPopulationLimit(); i++) {
+ pop.addChromosome(new DummyChromosome());
+ }
+
+ Population nextGeneration = pop.nextGeneration();
+
+ assertEquals(20, nextGeneration.getPopulationSize());
+ }
+
+ private static class DummyChromosome extends Chromosome {
+ private final int fitness;
+
+ public DummyChromosome() {
+ this.fitness = counter;
+ counter++;
+ }
+
+ public double fitness() {
+ return this.fitness;
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/FitnessCachingTest.java b/src/test/java/org/apache/commons/math/genetics/FitnessCachingTest.java
new file mode 100644
index 0000000..99d7b5a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/FitnessCachingTest.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.genetics;
+
+import static org.junit.Assert.*;
+
+import java.util.LinkedList;
+import java.util.List;
+import org.junit.Test;
+
+
+public class FitnessCachingTest {
+
+ // parameters for the GA
+ private static final int DIMENSION = 50;
+ private static final double CROSSOVER_RATE = 1;
+ private static final double MUTATION_RATE = 0.1;
+ private static final int TOURNAMENT_ARITY = 5;
+
+ private static final int POPULATION_SIZE = 10;
+ private static final int NUM_GENERATIONS = 50;
+ private static final double ELITISM_RATE = 0.2;
+
+ // how many times was the fitness computed
+ private static int fitnessCalls = 0;
+
+
+ @Test
+ public void testFitnessCaching() {
+ // initialize a new genetic algorithm
+ GeneticAlgorithm ga = new GeneticAlgorithm(
+ new OnePointCrossover<Integer>(),
+ CROSSOVER_RATE, // all selected chromosomes will be recombined (=crosssover)
+ new BinaryMutation(),
+ MUTATION_RATE, // no mutation
+ new TournamentSelection(TOURNAMENT_ARITY)
+ );
+
+ // initial population
+ Population initial = randomPopulation();
+ // stopping conditions
+ StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
+
+ // run the algorithm
+ ga.evolve(initial, stopCond);
+
+ int neededCalls =
+ POPULATION_SIZE /*initial population*/ +
+ (NUM_GENERATIONS - 1) /*for each population*/ * (int)(POPULATION_SIZE * (1.0 - ELITISM_RATE)) /*some chromosomes are copied*/
+ ;
+ assertTrue(fitnessCalls <= neededCalls); // some chromosomes after crossover may be the same os old ones
+ }
+
+
+ /**
+ * Initializes a random population.
+ */
+ private static ElitisticListPopulation randomPopulation() {
+ List<Chromosome> popList = new LinkedList<Chromosome>();
+
+ for (int i=0; i<POPULATION_SIZE; i++) {
+ BinaryChromosome randChrom = new DummyCountingBinaryChromosome(BinaryChromosome.randomBinaryRepresentation(DIMENSION));
+ popList.add(randChrom);
+ }
+ return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
+ }
+
+ private static class DummyCountingBinaryChromosome extends DummyBinaryChromosome {
+
+ public DummyCountingBinaryChromosome(List<Integer> representation) {
+ super(representation);
+ }
+
+ @Override
+ public double fitness() {
+ fitnessCalls++;
+ return 0;
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/FixedGenerationCountTest.java b/src/test/java/org/apache/commons/math/genetics/FixedGenerationCountTest.java
new file mode 100644
index 0000000..66cbe4c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/FixedGenerationCountTest.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;
+
+import static org.junit.Assert.*;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+public class FixedGenerationCountTest {
+
+ @Test
+ public void testIsSatisfied() {
+ FixedGenerationCount fgc = new FixedGenerationCount(20);
+
+ int cnt = 0;
+ Population pop = new Population() {
+ public void addChromosome(Chromosome chromosome) {
+ // unimportant
+ }
+ public Chromosome getFittestChromosome() {
+ // unimportant
+ return null;
+ }
+ public int getPopulationLimit() {
+ // unimportant
+ return 0;
+ }
+ public int getPopulationSize() {
+ // unimportant
+ return 0;
+ }
+ public Population nextGeneration() {
+ // unimportant
+ return null;
+ }
+ public Iterator<Chromosome> iterator() {
+ // unimportant
+ return null;
+ }
+ };
+
+ while (!fgc.isSatisfied(pop))
+ cnt++;
+ assertEquals(20, cnt);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestBinary.java b/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestBinary.java
new file mode 100644
index 0000000..8fffa56
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestBinary.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.genetics;
+
+import static org.junit.Assert.*;
+
+import java.util.LinkedList;
+import java.util.List;
+import org.junit.Test;
+
+/**
+ * This is also an example of usage.
+ */
+public class GeneticAlgorithmTestBinary {
+
+ // parameters for the GA
+ private static final int DIMENSION = 50;
+ private static final int POPULATION_SIZE = 50;
+ private static final int NUM_GENERATIONS = 50;
+ private static final double ELITISM_RATE = 0.2;
+ private static final double CROSSOVER_RATE = 1;
+ private static final double MUTATION_RATE = 0.1;
+ private static final int TOURNAMENT_ARITY = 2;
+
+ @Test
+ public void test() {
+ // to test a stochastic algorithm is hard, so this will rather be an usage example
+
+ // initialize a new genetic algorithm
+ GeneticAlgorithm ga = new GeneticAlgorithm(
+ new OnePointCrossover<Integer>(),
+ CROSSOVER_RATE, // all selected chromosomes will be recombined (=crosssover)
+ new BinaryMutation(),
+ MUTATION_RATE,
+ new TournamentSelection(TOURNAMENT_ARITY)
+ );
+
+ assertEquals(0, ga.getGenerationsEvolved());
+
+ // initial population
+ Population initial = randomPopulation();
+ // stopping conditions
+ StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
+
+ // best initial chromosome
+ Chromosome bestInitial = initial.getFittestChromosome();
+
+ // run the algorithm
+ Population finalPopulation = ga.evolve(initial, stopCond);
+
+ // best chromosome from the final population
+ Chromosome bestFinal = finalPopulation.getFittestChromosome();
+
+ // the only thing we can test is whether the final solution is not worse than the initial one
+ // however, for some implementations of GA, this need not be true :)
+
+ assertTrue(bestFinal.compareTo(bestInitial) > 0);
+ assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved());
+
+ }
+
+
+
+
+ /**
+ * Initializes a random population.
+ */
+ private static ElitisticListPopulation randomPopulation() {
+ List<Chromosome> popList = new LinkedList<Chromosome>();
+
+ for (int i=0; i<POPULATION_SIZE; i++) {
+ BinaryChromosome randChrom = new FindOnes(BinaryChromosome.randomBinaryRepresentation(DIMENSION));
+ popList.add(randChrom);
+ }
+ return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
+ }
+
+ /**
+ * Chromosomes represented by a binary chromosome.
+ *
+ * The goal is to set all bits (genes) to 1.
+ */
+ private static class FindOnes extends BinaryChromosome {
+
+ public FindOnes(List<Integer> representation) {
+ super(representation);
+ }
+
+ /**
+ * Returns number of elements != 0
+ */
+ public double fitness() {
+ int num = 0;
+ for (int val : this.getRepresentation()) {
+ if (val != 0)
+ num++;
+ }
+ // number of elements >= 0
+ return num;
+ }
+
+ @Override
+ public AbstractListChromosome<Integer> newFixedLengthChromosome(List<Integer> chromosomeRepresentation) {
+ return new FindOnes(chromosomeRepresentation);
+ }
+
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestPermutations.java b/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestPermutations.java
new file mode 100644
index 0000000..93dbcda
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/GeneticAlgorithmTestPermutations.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.genetics;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+/**
+ * This is also an example of usage.
+ *
+ * This algorithm does "stochastic sorting" of a sequence 0,...,N.
+ *
+ */
+public class GeneticAlgorithmTestPermutations {
+
+ // parameters for the GA
+ private static final int DIMENSION = 20;
+ private static final int POPULATION_SIZE = 80;
+ private static final int NUM_GENERATIONS = 200;
+ private static final double ELITISM_RATE = 0.2;
+ private static final double CROSSOVER_RATE = 1;
+ private static final double MUTATION_RATE = 0.08;
+ private static final int TOURNAMENT_ARITY = 2;
+
+ // numbers from 0 to N-1
+ private static final List<Integer> sequence = new ArrayList<Integer>();
+ static {
+ for (int i=0; i<DIMENSION; i++) {
+ sequence.add(i);
+ }
+ }
+
+ @Test
+ public void test() {
+ // to test a stochastic algorithm is hard, so this will rather be an usage example
+
+ // initialize a new genetic algorithm
+ GeneticAlgorithm ga = new GeneticAlgorithm(
+ new OnePointCrossover<Integer>(),
+ CROSSOVER_RATE,
+ new RandomKeyMutation(),
+ MUTATION_RATE,
+ new TournamentSelection(TOURNAMENT_ARITY)
+ );
+
+ // initial population
+ Population initial = randomPopulation();
+ // stopping conditions
+ StoppingCondition stopCond = new FixedGenerationCount(NUM_GENERATIONS);
+
+ // best initial chromosome
+ Chromosome bestInitial = initial.getFittestChromosome();
+
+ // run the algorithm
+ Population finalPopulation = ga.evolve(initial, stopCond);
+
+ // best chromosome from the final population
+ Chromosome bestFinal = finalPopulation.getFittestChromosome();
+
+ // the only thing we can test is whether the final solution is not worse than the initial one
+ // however, for some implementations of GA, this need not be true :)
+
+ assertTrue(bestFinal.compareTo(bestInitial) > 0);
+
+ //System.out.println(bestInitial);
+ //System.out.println(bestFinal);
+ }
+
+
+ /**
+ * Initializes a random population
+ */
+ private static ElitisticListPopulation randomPopulation() {
+ List<Chromosome> popList = new ArrayList<Chromosome>();
+ for (int i=0; i<POPULATION_SIZE; i++) {
+ Chromosome randChrom = new MinPermutations(RandomKey.randomPermutation(DIMENSION));
+ popList.add(randChrom);
+ }
+ return new ElitisticListPopulation(popList, popList.size(), ELITISM_RATE);
+ }
+
+ /**
+ * Chromosomes representing a permutation of (0,1,2,...,DIMENSION-1).
+ *
+ * The goal is to sort the sequence.
+ */
+ private static class MinPermutations extends RandomKey<Integer> {
+
+ public MinPermutations(List<Double> representation) {
+ super(representation);
+ }
+
+ public double fitness() {
+ int res = 0;
+ List<Integer> decoded = decode(sequence);
+ for (int i=0; i<decoded.size(); i++) {
+ int value = decoded.get(i);
+ if (value != i) {
+ // bad position found
+ res += FastMath.abs(value - i);
+ }
+ }
+ // the most fitted chromosome is the one with minimal error
+ // therefore we must return negative value
+ return -res;
+ }
+
+ @Override
+ public AbstractListChromosome<Double> newFixedLengthChromosome(List<Double> chromosomeRepresentation) {
+ return new MinPermutations(chromosomeRepresentation);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/ListPopulationTest.java b/src/test/java/org/apache/commons/math/genetics/ListPopulationTest.java
new file mode 100644
index 0000000..f43ef95
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/ListPopulationTest.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.genetics;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+public class ListPopulationTest {
+
+ @Test
+ public void testGetFittestChromosome() {
+ Chromosome c1 = new Chromosome() {
+ public double fitness() {
+ return 0;
+ }
+ };
+ Chromosome c2 = new Chromosome() {
+ public double fitness() {
+ return 10;
+ }
+ };
+ Chromosome c3 = new Chromosome() {
+ public double fitness() {
+ return 15;
+ }
+ };
+
+ ArrayList<Chromosome> chromosomes = new ArrayList<Chromosome> ();
+ chromosomes.add(c1);
+ chromosomes.add(c2);
+ chromosomes.add(c3);
+
+ ListPopulation population = new ListPopulation(chromosomes,10) {
+
+ public Population nextGeneration() {
+ // not important
+ return null;
+ }
+ };
+
+ assertEquals(c3, population.getFittestChromosome());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/OnePointCrossoverTest.java b/src/test/java/org/apache/commons/math/genetics/OnePointCrossoverTest.java
new file mode 100644
index 0000000..a332498
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/OnePointCrossoverTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.genetics;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+public class OnePointCrossoverTest {
+
+ @Test
+ public void testCrossover() {
+ Integer[] p1 = new Integer[] {1,0,1,0,0,1,0,1,1};
+ Integer[] p2 = new Integer[] {0,1,1,0,1,0,1,1,1};
+
+ BinaryChromosome p1c = new DummyBinaryChromosome(p1);
+ BinaryChromosome p2c = new DummyBinaryChromosome(p2);
+
+ OnePointCrossover<Integer> opc = new OnePointCrossover<Integer>();
+
+ // how to test a stochastic method?
+ for (int i=0; i<20; i++) {
+ ChromosomePair pair = opc.crossover(p1c,p2c);
+
+ Integer[] c1 = new Integer[p1.length];
+ Integer[] c2 = new Integer[p2.length];
+
+ c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation().toArray(c1);
+ c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation().toArray(c2);
+
+ // first and last values will be the same
+ assertEquals((int) p1[0], (int) c1[0]);
+ assertEquals((int) p2[0], (int) c2[0]);
+ assertEquals((int) p1[p1.length-1], (int) c1[c1.length-1]);
+ assertEquals((int) p2[p2.length-1], (int) c2[c2.length-1]);
+ // moreover, in the above setting, the 2nd, 3rd and 7th values will be the same
+ assertEquals((int) p1[2], (int) c1[2]);
+ assertEquals((int) p2[2], (int) c2[2]);
+ assertEquals((int) p1[3], (int) c1[3]);
+ assertEquals((int) p2[3], (int) c2[3]);
+ assertEquals((int) p1[7], (int) c1[7]);
+ assertEquals((int) p2[7], (int) c2[7]);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/RandomKeyMutationTest.java b/src/test/java/org/apache/commons/math/genetics/RandomKeyMutationTest.java
new file mode 100644
index 0000000..dc5bab5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/RandomKeyMutationTest.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 static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class RandomKeyMutationTest {
+
+ @Test
+ public void testMutate() {
+ MutationPolicy mutation = new RandomKeyMutation();
+ int l=10;
+ for (int i=0; i<20; i++) {
+ DummyRandomKey origRk = new DummyRandomKey(RandomKey.randomPermutation(l));
+ Chromosome mutated = mutation.mutate(origRk);
+ DummyRandomKey mutatedRk = (DummyRandomKey) mutated;
+
+ int changes = 0;
+ for (int j=0; j<origRk.getLength(); j++) {
+ if (origRk.getRepresentation().get(j) != mutatedRk.getRepresentation().get(j)) {
+ changes++;
+ }
+ }
+ assertEquals(1,changes);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/RandomKeyTest.java b/src/test/java/org/apache/commons/math/genetics/RandomKeyTest.java
new file mode 100644
index 0000000..887d5c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/RandomKeyTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.genetics;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import org.junit.Test;
+
+public class RandomKeyTest {
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testConstructor1() {
+ new DummyRandomKey(new Double[] {0.2, 0.3, 1.2});
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testConstructor2() {
+ new DummyRandomKey(new Double[] {0.2, 0.3, -0.2});
+ }
+
+ @Test
+ public void testIsSame() {
+ DummyRandomKey drk1 = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
+ DummyRandomKey drk2 = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
+ DummyRandomKey drk3 = new DummyRandomKey(new Double[] {0.4, 0.15, 0.5, 0.8, 0.2});
+ DummyRandomKey drk4 = new DummyRandomKey(new Double[] {0.4, 0.25, 0.5, 0.8, 0.2});
+ DummyRandomKey drk5 = new DummyRandomKey(new Double[] {0.4, 0.25, 0.5, 0.8, 0.2, 0.5});
+
+ assertTrue(drk1.isSame(drk2));
+ assertTrue(drk2.isSame(drk3));
+ assertFalse(drk3.isSame(drk4));
+ assertFalse(drk4.isSame(drk5));
+ }
+
+ @Test
+ public void testDecode() {
+ DummyRandomKey drk = new DummyRandomKey(new Double[] {0.4, 0.1, 0.5, 0.8, 0.2});
+ List<String> decoded = drk.decode(Arrays.asList(new String[] {"a", "b", "c", "d", "e"}));
+
+ assertEquals("b", decoded.get(0));
+ assertEquals("e", decoded.get(1));
+ assertEquals("a", decoded.get(2));
+ assertEquals("c", decoded.get(3));
+ assertEquals("d", decoded.get(4));
+ }
+
+ @Test
+ public void testRandomPermutation() {
+ // never generate an invalid one
+ for (int i=0; i<10; i++) {
+ DummyRandomKey drk = new DummyRandomKey(RandomKey.randomPermutation(20));
+ assertNotNull(drk);
+ }
+ }
+
+ @Test
+ public void testIdentityPermutation() {
+ DummyRandomKey drk = new DummyRandomKey(RandomKey.identityPermutation(5));
+ List<String> decoded = drk.decode(Arrays.asList(new String[] {"a", "b", "c", "d", "e"}));
+
+ assertEquals("a", decoded.get(0));
+ assertEquals("b", decoded.get(1));
+ assertEquals("c", decoded.get(2));
+ assertEquals("d", decoded.get(3));
+ assertEquals("e", decoded.get(4));
+ }
+
+ @Test
+ public void testComparatorPermutation() {
+ List<String> data = Arrays.asList(new String[] {"x", "b", "c", "z", "b"});
+
+ List<Double> permutation = RandomKey.comparatorPermutation(data, new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ return o1.compareTo(o2);
+ }
+ });
+ Double[] permArr = new Double[data.size()];
+ permArr = permutation.toArray(permArr);
+ assertArrayEquals(new Double[] {0.6,0.0,0.4,0.8,0.2}, permArr);
+ List<String> decodedData = new DummyRandomKey(permutation).decode(data);
+ assertEquals("b", decodedData.get(0));
+ assertEquals("b", decodedData.get(1));
+ assertEquals("c", decodedData.get(2));
+ assertEquals("x", decodedData.get(3));
+ assertEquals("z", decodedData.get(4));
+
+ permutation = RandomKey.comparatorPermutation(data, new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ return o2.compareTo(o1);
+ }
+ });
+ permArr = new Double[data.size()];
+ permArr = permutation.toArray(permArr);
+ assertArrayEquals(new Double[] {0.2,0.6,0.4,0.0,0.8}, permArr);
+ decodedData = new DummyRandomKey(permutation).decode(data);
+ assertEquals("z", decodedData.get(0));
+ assertEquals("x", decodedData.get(1));
+ assertEquals("c", decodedData.get(2));
+ assertEquals("b", decodedData.get(3));
+ assertEquals("b", decodedData.get(4));
+ }
+
+ @Test
+ public void testInducedPermutation() {
+ List<String> origData = Arrays.asList(new String[] {"a", "b", "c", "d", "d"});
+ List<String> permutedData = Arrays.asList(new String[] {"d", "b", "c", "a", "d"});
+
+ DummyRandomKey drk = new DummyRandomKey(RandomKey.inducedPermutation(origData, permutedData));
+ List<String> decoded = drk.decode(origData);
+
+ assertEquals("d", decoded.get(0));
+ assertEquals("b", decoded.get(1));
+ assertEquals("c", decoded.get(2));
+ assertEquals("a", decoded.get(3));
+ assertEquals("d", decoded.get(4));
+
+ try {
+ RandomKey.inducedPermutation(
+ Arrays.asList(new String[] {"a", "b", "c", "d", "d"}),
+ Arrays.asList(new String[] {"a", "b", "c", "d"})
+ );
+ fail("Uncaught exception");
+ } catch (IllegalArgumentException e) {
+ // no-op
+ }
+ try {
+ RandomKey.inducedPermutation(
+ Arrays.asList(new String[] {"a", "b", "c", "d", "d"}),
+ Arrays.asList(new String[] {"a", "b", "c", "d", "f"})
+ );
+ fail("Uncaught exception");
+ } catch (IllegalArgumentException e) {
+ // no-op
+ }
+ }
+
+ @Test
+ public void testEqualRepr() {
+ DummyRandomKey drk = new DummyRandomKey(new Double[] {0.2, 0.2, 0.5});
+ List<String> decodedData = drk.decode(Arrays.asList(new String[] {"a", "b", "c"}));
+ assertEquals("a", decodedData.get(0));
+ assertEquals("b", decodedData.get(1));
+ assertEquals("c", decodedData.get(2));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/genetics/TournamentSelectionTest.java b/src/test/java/org/apache/commons/math/genetics/TournamentSelectionTest.java
new file mode 100644
index 0000000..d6b3f7a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/genetics/TournamentSelectionTest.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.genetics;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+public class TournamentSelectionTest {
+
+ private static int counter = 0;
+
+ @Test
+ public void testSelect() {
+ TournamentSelection ts = new TournamentSelection(2);
+ ElitisticListPopulation pop = new ElitisticListPopulation(100, 0.203);
+
+ for (int i=0; i<pop.getPopulationLimit(); i++) {
+ pop.addChromosome(new DummyChromosome());
+ }
+ // how to write a test for stochastic method?
+ for (int i=0; i<20; i++) {
+ ChromosomePair pair = ts.select(pop);
+ // the worst chromosome should NEVER be selected
+ assertTrue(pair.getFirst().getFitness() > 0);
+ assertTrue(pair.getSecond().getFitness() > 0);
+ }
+ }
+
+ private static class DummyChromosome extends Chromosome {
+ private final int fitness;
+
+ public DummyChromosome() {
+ this.fitness = counter;
+ counter++;
+ }
+
+ public double fitness() {
+ return this.fitness;
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/FrenchVector3DFormatTest.java b/src/test/java/org/apache/commons/math/geometry/FrenchVector3DFormatTest.java
new file mode 100644
index 0000000..c70b1d9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/FrenchVector3DFormatTest.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.geometry;
+
+import java.util.Locale;
+
+
+public class FrenchVector3DFormatTest extends Vector3DFormatAbstractTest {
+
+ @Override
+ protected char getDecimalCharacter() {
+ return ',';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.FRENCH;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/RotationOrderTest.java b/src/test/java/org/apache/commons/math/geometry/RotationOrderTest.java
new file mode 100644
index 0000000..d80f341
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/RotationOrderTest.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.geometry;
+
+import java.lang.reflect.Field;
+
+import org.apache.commons.math.geometry.RotationOrder;
+
+import junit.framework.*;
+
+public class RotationOrderTest
+ extends TestCase {
+
+ public RotationOrderTest(String name) {
+ super(name);
+ }
+
+ public void testName() {
+
+ RotationOrder[] orders = {
+ RotationOrder.XYZ, RotationOrder.XZY, RotationOrder.YXZ,
+ RotationOrder.YZX, RotationOrder.ZXY, RotationOrder.ZYX,
+ RotationOrder.XYX, RotationOrder.XZX, RotationOrder.YXY,
+ RotationOrder.YZY, RotationOrder.ZXZ, RotationOrder.ZYZ
+ };
+
+ for (int i = 0; i < orders.length; ++i) {
+ assertEquals(getFieldName(orders[i]), orders[i].toString());
+ }
+
+ }
+
+ private String getFieldName(RotationOrder order) {
+ try {
+ Field[] fields = RotationOrder.class.getFields();
+ for (int i = 0; i < fields.length; ++i) {
+ if (fields[i].get(null) == order) {
+ return fields[i].getName();
+ }
+ }
+ } catch (IllegalAccessException iae) {
+ // ignored
+ }
+ return "unknown";
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/RotationTest.java b/src/test/java/org/apache/commons/math/geometry/RotationTest.java
new file mode 100644
index 0000000..9730fbd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/RotationTest.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.geometry.CardanEulerSingularityException;
+import org.apache.commons.math.geometry.NotARotationMatrixException;
+import org.apache.commons.math.geometry.Rotation;
+import org.apache.commons.math.geometry.RotationOrder;
+import org.apache.commons.math.geometry.Vector3D;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+import junit.framework.*;
+
+public class RotationTest
+ extends TestCase {
+
+ public RotationTest(String name) {
+ super(name);
+ }
+
+ public void testIdentity() {
+
+ Rotation r = Rotation.IDENTITY;
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_I);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.PLUS_J);
+ checkVector(r.applyTo(Vector3D.PLUS_K), Vector3D.PLUS_K);
+ checkAngle(r.getAngle(), 0);
+
+ r = new Rotation(-1, 0, 0, 0, false);
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_I);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.PLUS_J);
+ checkVector(r.applyTo(Vector3D.PLUS_K), Vector3D.PLUS_K);
+ checkAngle(r.getAngle(), 0);
+
+ r = new Rotation(42, 0, 0, 0, true);
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_I);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.PLUS_J);
+ checkVector(r.applyTo(Vector3D.PLUS_K), Vector3D.PLUS_K);
+ checkAngle(r.getAngle(), 0);
+
+ }
+
+ public void testAxisAngle() {
+
+ Rotation r = new Rotation(new Vector3D(10, 10, 10), 2 * FastMath.PI / 3);
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_J);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.PLUS_K);
+ checkVector(r.applyTo(Vector3D.PLUS_K), Vector3D.PLUS_I);
+ double s = 1 / FastMath.sqrt(3);
+ checkVector(r.getAxis(), new Vector3D(s, s, s));
+ checkAngle(r.getAngle(), 2 * FastMath.PI / 3);
+
+ try {
+ new Rotation(new Vector3D(0, 0, 0), 2 * FastMath.PI / 3);
+ fail("an exception should have been thrown");
+ } catch (ArithmeticException e) {
+ }
+
+ r = new Rotation(Vector3D.PLUS_K, 1.5 * FastMath.PI);
+ checkVector(r.getAxis(), new Vector3D(0, 0, -1));
+ checkAngle(r.getAngle(), 0.5 * FastMath.PI);
+
+ r = new Rotation(Vector3D.PLUS_J, FastMath.PI);
+ checkVector(r.getAxis(), Vector3D.PLUS_J);
+ checkAngle(r.getAngle(), FastMath.PI);
+
+ checkVector(Rotation.IDENTITY.getAxis(), Vector3D.PLUS_I);
+
+ }
+
+ public void testRevert() {
+ Rotation r = new Rotation(0.001, 0.36, 0.48, 0.8, true);
+ Rotation reverted = r.revert();
+ checkRotation(r.applyTo(reverted), 1, 0, 0, 0);
+ checkRotation(reverted.applyTo(r), 1, 0, 0, 0);
+ assertEquals(r.getAngle(), reverted.getAngle(), 1.0e-12);
+ assertEquals(-1, Vector3D.dotProduct(r.getAxis(), reverted.getAxis()), 1.0e-12);
+ }
+
+ public void testVectorOnePair() {
+
+ Vector3D u = new Vector3D(3, 2, 1);
+ Vector3D v = new Vector3D(-4, 2, 2);
+ Rotation r = new Rotation(u, v);
+ checkVector(r.applyTo(u.scalarMultiply(v.getNorm())), v.scalarMultiply(u.getNorm()));
+
+ checkAngle(new Rotation(u, u.negate()).getAngle(), FastMath.PI);
+
+ try {
+ new Rotation(u, Vector3D.ZERO);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ }
+
+ public void testVectorTwoPairs() {
+
+ Vector3D u1 = new Vector3D(3, 0, 0);
+ Vector3D u2 = new Vector3D(0, 5, 0);
+ Vector3D v1 = new Vector3D(0, 0, 2);
+ Vector3D v2 = new Vector3D(-2, 0, 2);
+ Rotation r = new Rotation(u1, u2, v1, v2);
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_K);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.MINUS_I);
+
+ r = new Rotation(u1, u2, u1.negate(), u2.negate());
+ Vector3D axis = r.getAxis();
+ if (Vector3D.dotProduct(axis, Vector3D.PLUS_K) > 0) {
+ checkVector(axis, Vector3D.PLUS_K);
+ } else {
+ checkVector(axis, Vector3D.MINUS_K);
+ }
+ checkAngle(r.getAngle(), FastMath.PI);
+
+ double sqrt = FastMath.sqrt(2) / 2;
+ r = new Rotation(Vector3D.PLUS_I, Vector3D.PLUS_J,
+ new Vector3D(0.5, 0.5, sqrt),
+ new Vector3D(0.5, 0.5, -sqrt));
+ checkRotation(r, sqrt, 0.5, 0.5, 0);
+
+ r = new Rotation(u1, u2, u1, Vector3D.crossProduct(u1, u2));
+ checkRotation(r, sqrt, -sqrt, 0, 0);
+
+ checkRotation(new Rotation(u1, u2, u1, u2), 1, 0, 0, 0);
+
+ try {
+ new Rotation(u1, u2, Vector3D.ZERO, v2);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ }
+
+ public void testMatrix()
+ throws NotARotationMatrixException {
+
+ try {
+ new Rotation(new double[][] {
+ { 0.0, 1.0, 0.0 },
+ { 1.0, 0.0, 0.0 }
+ }, 1.0e-7);
+ fail("Expecting NotARotationMatrixException");
+ } catch (NotARotationMatrixException nrme) {
+ // expected behavior
+ }
+
+ try {
+ new Rotation(new double[][] {
+ { 0.445888, 0.797184, -0.407040 },
+ { 0.821760, -0.184320, 0.539200 },
+ { -0.354816, 0.574912, 0.737280 }
+ }, 1.0e-7);
+ fail("Expecting NotARotationMatrixException");
+ } catch (NotARotationMatrixException nrme) {
+ // expected behavior
+ }
+
+ try {
+ new Rotation(new double[][] {
+ { 0.4, 0.8, -0.4 },
+ { -0.4, 0.6, 0.7 },
+ { 0.8, -0.2, 0.5 }
+ }, 1.0e-15);
+ fail("Expecting NotARotationMatrixException");
+ } catch (NotARotationMatrixException nrme) {
+ // expected behavior
+ }
+
+ checkRotation(new Rotation(new double[][] {
+ { 0.445888, 0.797184, -0.407040 },
+ { -0.354816, 0.574912, 0.737280 },
+ { 0.821760, -0.184320, 0.539200 }
+ }, 1.0e-10),
+ 0.8, 0.288, 0.384, 0.36);
+
+ checkRotation(new Rotation(new double[][] {
+ { 0.539200, 0.737280, 0.407040 },
+ { 0.184320, -0.574912, 0.797184 },
+ { 0.821760, -0.354816, -0.445888 }
+ }, 1.0e-10),
+ 0.36, 0.8, 0.288, 0.384);
+
+ checkRotation(new Rotation(new double[][] {
+ { -0.445888, 0.797184, -0.407040 },
+ { 0.354816, 0.574912, 0.737280 },
+ { 0.821760, 0.184320, -0.539200 }
+ }, 1.0e-10),
+ 0.384, 0.36, 0.8, 0.288);
+
+ checkRotation(new Rotation(new double[][] {
+ { -0.539200, 0.737280, 0.407040 },
+ { -0.184320, -0.574912, 0.797184 },
+ { 0.821760, 0.354816, 0.445888 }
+ }, 1.0e-10),
+ 0.288, 0.384, 0.36, 0.8);
+
+ double[][] m1 = { { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0 } };
+ Rotation r = new Rotation(m1, 1.0e-7);
+ checkVector(r.applyTo(Vector3D.PLUS_I), Vector3D.PLUS_K);
+ checkVector(r.applyTo(Vector3D.PLUS_J), Vector3D.PLUS_I);
+ checkVector(r.applyTo(Vector3D.PLUS_K), Vector3D.PLUS_J);
+
+ double[][] m2 = { { 0.83203, -0.55012, -0.07139 },
+ { 0.48293, 0.78164, -0.39474 },
+ { 0.27296, 0.29396, 0.91602 } };
+ r = new Rotation(m2, 1.0e-12);
+
+ double[][] m3 = r.getMatrix();
+ double d00 = m2[0][0] - m3[0][0];
+ double d01 = m2[0][1] - m3[0][1];
+ double d02 = m2[0][2] - m3[0][2];
+ double d10 = m2[1][0] - m3[1][0];
+ double d11 = m2[1][1] - m3[1][1];
+ double d12 = m2[1][2] - m3[1][2];
+ double d20 = m2[2][0] - m3[2][0];
+ double d21 = m2[2][1] - m3[2][1];
+ double d22 = m2[2][2] - m3[2][2];
+
+ assertTrue(FastMath.abs(d00) < 6.0e-6);
+ assertTrue(FastMath.abs(d01) < 6.0e-6);
+ assertTrue(FastMath.abs(d02) < 6.0e-6);
+ assertTrue(FastMath.abs(d10) < 6.0e-6);
+ assertTrue(FastMath.abs(d11) < 6.0e-6);
+ assertTrue(FastMath.abs(d12) < 6.0e-6);
+ assertTrue(FastMath.abs(d20) < 6.0e-6);
+ assertTrue(FastMath.abs(d21) < 6.0e-6);
+ assertTrue(FastMath.abs(d22) < 6.0e-6);
+
+ assertTrue(FastMath.abs(d00) > 4.0e-7);
+ assertTrue(FastMath.abs(d01) > 4.0e-7);
+ assertTrue(FastMath.abs(d02) > 4.0e-7);
+ assertTrue(FastMath.abs(d10) > 4.0e-7);
+ assertTrue(FastMath.abs(d11) > 4.0e-7);
+ assertTrue(FastMath.abs(d12) > 4.0e-7);
+ assertTrue(FastMath.abs(d20) > 4.0e-7);
+ assertTrue(FastMath.abs(d21) > 4.0e-7);
+ assertTrue(FastMath.abs(d22) > 4.0e-7);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ double m3tm3 = m3[i][0] * m3[j][0]
+ + m3[i][1] * m3[j][1]
+ + m3[i][2] * m3[j][2];
+ if (i == j) {
+ assertTrue(FastMath.abs(m3tm3 - 1.0) < 1.0e-10);
+ } else {
+ assertTrue(FastMath.abs(m3tm3) < 1.0e-10);
+ }
+ }
+ }
+
+ checkVector(r.applyTo(Vector3D.PLUS_I),
+ new Vector3D(m3[0][0], m3[1][0], m3[2][0]));
+ checkVector(r.applyTo(Vector3D.PLUS_J),
+ new Vector3D(m3[0][1], m3[1][1], m3[2][1]));
+ checkVector(r.applyTo(Vector3D.PLUS_K),
+ new Vector3D(m3[0][2], m3[1][2], m3[2][2]));
+
+ double[][] m4 = { { 1.0, 0.0, 0.0 },
+ { 0.0, -1.0, 0.0 },
+ { 0.0, 0.0, -1.0 } };
+ r = new Rotation(m4, 1.0e-7);
+ checkAngle(r.getAngle(), FastMath.PI);
+
+ try {
+ double[][] m5 = { { 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0 },
+ { 1.0, 0.0, 0.0 } };
+ r = new Rotation(m5, 1.0e-7);
+ fail("got " + r + ", should have caught an exception");
+ } catch (NotARotationMatrixException e) {
+ // expected
+ }
+
+ }
+
+ public void testAngles()
+ throws CardanEulerSingularityException {
+
+ RotationOrder[] CardanOrders = {
+ RotationOrder.XYZ, RotationOrder.XZY, RotationOrder.YXZ,
+ RotationOrder.YZX, RotationOrder.ZXY, RotationOrder.ZYX
+ };
+
+ for (int i = 0; i < CardanOrders.length; ++i) {
+ for (double alpha1 = 0.1; alpha1 < 6.2; alpha1 += 0.3) {
+ for (double alpha2 = -1.55; alpha2 < 1.55; alpha2 += 0.3) {
+ for (double alpha3 = 0.1; alpha3 < 6.2; alpha3 += 0.3) {
+ Rotation r = new Rotation(CardanOrders[i], alpha1, alpha2, alpha3);
+ double[] angles = r.getAngles(CardanOrders[i]);
+ checkAngle(angles[0], alpha1);
+ checkAngle(angles[1], alpha2);
+ checkAngle(angles[2], alpha3);
+ }
+ }
+ }
+ }
+
+ RotationOrder[] EulerOrders = {
+ RotationOrder.XYX, RotationOrder.XZX, RotationOrder.YXY,
+ RotationOrder.YZY, RotationOrder.ZXZ, RotationOrder.ZYZ
+ };
+
+ for (int i = 0; i < EulerOrders.length; ++i) {
+ for (double alpha1 = 0.1; alpha1 < 6.2; alpha1 += 0.3) {
+ for (double alpha2 = 0.05; alpha2 < 3.1; alpha2 += 0.3) {
+ for (double alpha3 = 0.1; alpha3 < 6.2; alpha3 += 0.3) {
+ Rotation r = new Rotation(EulerOrders[i],
+ alpha1, alpha2, alpha3);
+ double[] angles = r.getAngles(EulerOrders[i]);
+ checkAngle(angles[0], alpha1);
+ checkAngle(angles[1], alpha2);
+ checkAngle(angles[2], alpha3);
+ }
+ }
+ }
+ }
+
+ }
+
+ public void testSingularities() {
+
+ RotationOrder[] CardanOrders = {
+ RotationOrder.XYZ, RotationOrder.XZY, RotationOrder.YXZ,
+ RotationOrder.YZX, RotationOrder.ZXY, RotationOrder.ZYX
+ };
+
+ double[] singularCardanAngle = { FastMath.PI / 2, -FastMath.PI / 2 };
+ for (int i = 0; i < CardanOrders.length; ++i) {
+ for (int j = 0; j < singularCardanAngle.length; ++j) {
+ Rotation r = new Rotation(CardanOrders[i], 0.1, singularCardanAngle[j], 0.3);
+ try {
+ r.getAngles(CardanOrders[i]);
+ fail("an exception should have been caught");
+ } catch (CardanEulerSingularityException cese) {
+ // expected behavior
+ }
+ }
+ }
+
+ RotationOrder[] EulerOrders = {
+ RotationOrder.XYX, RotationOrder.XZX, RotationOrder.YXY,
+ RotationOrder.YZY, RotationOrder.ZXZ, RotationOrder.ZYZ
+ };
+
+ double[] singularEulerAngle = { 0, FastMath.PI };
+ for (int i = 0; i < EulerOrders.length; ++i) {
+ for (int j = 0; j < singularEulerAngle.length; ++j) {
+ Rotation r = new Rotation(EulerOrders[i], 0.1, singularEulerAngle[j], 0.3);
+ try {
+ r.getAngles(EulerOrders[i]);
+ fail("an exception should have been caught");
+ } catch (CardanEulerSingularityException cese) {
+ // expected behavior
+ }
+ }
+ }
+
+
+ }
+
+ public void testQuaternion() {
+
+ Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7);
+ double n = 23.5;
+ Rotation r2 = new Rotation(n * r1.getQ0(), n * r1.getQ1(),
+ n * r1.getQ2(), n * r1.getQ3(),
+ true);
+ for (double x = -0.9; x < 0.9; x += 0.2) {
+ for (double y = -0.9; y < 0.9; y += 0.2) {
+ for (double z = -0.9; z < 0.9; z += 0.2) {
+ Vector3D u = new Vector3D(x, y, z);
+ checkVector(r2.applyTo(u), r1.applyTo(u));
+ }
+ }
+ }
+
+ r1 = new Rotation( 0.288, 0.384, 0.36, 0.8, false);
+ checkRotation(r1, -r1.getQ0(), -r1.getQ1(), -r1.getQ2(), -r1.getQ3());
+
+ }
+
+ public void testCompose() {
+
+ Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7);
+ Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3);
+ Rotation r3 = r2.applyTo(r1);
+
+ for (double x = -0.9; x < 0.9; x += 0.2) {
+ for (double y = -0.9; y < 0.9; y += 0.2) {
+ for (double z = -0.9; z < 0.9; z += 0.2) {
+ Vector3D u = new Vector3D(x, y, z);
+ checkVector(r2.applyTo(r1.applyTo(u)), r3.applyTo(u));
+ }
+ }
+ }
+
+ }
+
+ public void testComposeInverse() {
+
+ Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7);
+ Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3);
+ Rotation r3 = r2.applyInverseTo(r1);
+
+ for (double x = -0.9; x < 0.9; x += 0.2) {
+ for (double y = -0.9; y < 0.9; y += 0.2) {
+ for (double z = -0.9; z < 0.9; z += 0.2) {
+ Vector3D u = new Vector3D(x, y, z);
+ checkVector(r2.applyInverseTo(r1.applyTo(u)), r3.applyTo(u));
+ }
+ }
+ }
+
+ }
+
+ public void testApplyInverseTo() {
+
+ Rotation r = new Rotation(new Vector3D(2, -3, 5), 1.7);
+ for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
+ for (double phi = -1.55; phi < 1.55; phi += 0.2) {
+ Vector3D u = new Vector3D(FastMath.cos(lambda) * FastMath.cos(phi),
+ FastMath.sin(lambda) * FastMath.cos(phi),
+ FastMath.sin(phi));
+ r.applyInverseTo(r.applyTo(u));
+ checkVector(u, r.applyInverseTo(r.applyTo(u)));
+ checkVector(u, r.applyTo(r.applyInverseTo(u)));
+ }
+ }
+
+ r = Rotation.IDENTITY;
+ for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
+ for (double phi = -1.55; phi < 1.55; phi += 0.2) {
+ Vector3D u = new Vector3D(FastMath.cos(lambda) * FastMath.cos(phi),
+ FastMath.sin(lambda) * FastMath.cos(phi),
+ FastMath.sin(phi));
+ checkVector(u, r.applyInverseTo(r.applyTo(u)));
+ checkVector(u, r.applyTo(r.applyInverseTo(u)));
+ }
+ }
+
+ r = new Rotation(Vector3D.PLUS_K, FastMath.PI);
+ for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
+ for (double phi = -1.55; phi < 1.55; phi += 0.2) {
+ Vector3D u = new Vector3D(FastMath.cos(lambda) * FastMath.cos(phi),
+ FastMath.sin(lambda) * FastMath.cos(phi),
+ FastMath.sin(phi));
+ checkVector(u, r.applyInverseTo(r.applyTo(u)));
+ checkVector(u, r.applyTo(r.applyInverseTo(u)));
+ }
+ }
+
+ }
+
+ private void checkVector(Vector3D v1, Vector3D v2) {
+ assertTrue(v1.subtract(v2).getNorm() < 1.0e-10);
+ }
+
+ private void checkAngle(double a1, double a2) {
+ assertEquals(a1, MathUtils.normalizeAngle(a2, a1), 1.0e-10);
+ }
+
+ private void checkRotation(Rotation r, double q0, double q1, double q2, double q3) {
+ assertEquals(0, Rotation.distance(r, new Rotation(q0, q1, q2, q3, false)), 1.0e-12);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/Vector3DFormatAbstractTest.java b/src/test/java/org/apache/commons/math/geometry/Vector3DFormatAbstractTest.java
new file mode 100644
index 0000000..ff32187
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/Vector3DFormatAbstractTest.java
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.geometry;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.util.CompositeFormat;
+
+public abstract class Vector3DFormatAbstractTest extends TestCase {
+
+ Vector3DFormat vector3DFormat = null;
+ Vector3DFormat vector3DFormatSquare = null;
+
+ protected abstract Locale getLocale();
+
+ protected abstract char getDecimalCharacter();
+
+ @Override
+ protected void setUp() throws Exception {
+ vector3DFormat = Vector3DFormat.getInstance(getLocale());
+ final NumberFormat nf = NumberFormat.getInstance(getLocale());
+ nf.setMaximumFractionDigits(2);
+ vector3DFormatSquare = new Vector3DFormat("[", "]", " : ", nf);
+ }
+
+ public void testSimpleNoDecimals() {
+ Vector3D c = new Vector3D(1, 1, 1);
+ String expected = "{1; 1; 1}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimals() {
+ Vector3D c = new Vector3D(1.23, 1.43, 1.63);
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimalsTrunc() {
+ Vector3D c = new Vector3D(1.2323, 1.4343, 1.6333);
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeX() {
+ Vector3D c = new Vector3D(-1.2323, 1.4343, 1.6333);
+ String expected =
+ "{-1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeY() {
+ Vector3D c = new Vector3D(1.2323, -1.4343, 1.6333);
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; -1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeZ() {
+ Vector3D c = new Vector3D(1.2323, 1.4343, -1.6333);
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; -1" + getDecimalCharacter() +
+ "63}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNonDefaultSetting() {
+ Vector3D c = new Vector3D(1, 1, 1);
+ String expected = "[1 : 1 : 1]";
+ String actual = vector3DFormatSquare.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testStaticFormatVector3D() {
+ Locale defaultLocal = Locale.getDefault();
+ Locale.setDefault(getLocale());
+
+ Vector3D c = new Vector3D(232.222, -342.33, 432.444);
+ String expected =
+ "{232" + getDecimalCharacter() +
+ "22; -342" + getDecimalCharacter() +
+ "33; 432" + getDecimalCharacter() +
+ "44}";
+ String actual = Vector3DFormat.formatVector3D(c);
+ assertEquals(expected, actual);
+
+ Locale.setDefault(defaultLocal);
+ }
+
+ public void testNan() {
+ Vector3D c = Vector3D.NaN;
+ String expected = "{(NaN); (NaN); (NaN)}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testPositiveInfinity() {
+ Vector3D c = Vector3D.POSITIVE_INFINITY;
+ String expected = "{(Infinity); (Infinity); (Infinity)}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void tesNegativeInfinity() {
+ Vector3D c = Vector3D.NEGATIVE_INFINITY;
+ String expected = "{(-Infinity); (-Infinity); (-Infinity)}";
+ String actual = vector3DFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testParseSimpleNoDecimals() {
+ String source = "{1; 1; 1}";
+ Vector3D expected = new Vector3D(1, 1, 1);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseIgnoredWhitespace() {
+ Vector3D expected = new Vector3D(1, 1, 1);
+ ParsePosition pos1 = new ParsePosition(0);
+ String source1 = "{1;1;1}";
+ assertEquals(expected, vector3DFormat.parseObject(source1, pos1));
+ assertEquals(source1.length(), pos1.getIndex());
+ ParsePosition pos2 = new ParsePosition(0);
+ String source2 = " { 1 ; 1 ; 1 } ";
+ assertEquals(expected, vector3DFormat.parseObject(source2, pos2));
+ assertEquals(source2.length() - 1, pos2.getIndex());
+ }
+
+ public void testParseSimpleWithDecimals() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ Vector3D expected = new Vector3D(1.23, 1.43, 1.63);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseSimpleWithDecimalsTrunc() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(1.2323, 1.4343, 1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeX() {
+ String source =
+ "{-1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(-1.2323, 1.4343, 1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeY() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; -1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(1.2323, -1.4343, 1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeZ() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; -1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(1.2323, 1.4343, -1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeAll() {
+ String source =
+ "{-1" + getDecimalCharacter() +
+ "2323; -1" + getDecimalCharacter() +
+ "4343; -1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(-1.2323, -1.4343, -1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseZeroX() {
+ String source =
+ "{0" + getDecimalCharacter() +
+ "0; -1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ Vector3D expected = new Vector3D(0.0, -1.4343, 1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNonDefaultSetting() {
+ String source =
+ "[1" + getDecimalCharacter() +
+ "2323 : 1" + getDecimalCharacter() +
+ "4343 : 1" + getDecimalCharacter() +
+ "6333]";
+ Vector3D expected = new Vector3D(1.2323, 1.4343, 1.6333);
+ try {
+ Vector3D actual = (Vector3D) vector3DFormatSquare.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNan() {
+ String source = "{(NaN); (NaN); (NaN)}";
+ try {
+ Vector3D actual = (Vector3D) vector3DFormat.parseObject(source);
+ assertEquals(Vector3D.NaN, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParsePositiveInfinity() {
+ String source = "{(Infinity); (Infinity); (Infinity)}";
+ try {
+ Vector3D actual = (Vector3D)vector3DFormat.parseObject(source);
+ assertEquals(Vector3D.POSITIVE_INFINITY, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeInfinity() {
+ String source = "{(-Infinity); (-Infinity); (-Infinity)}";
+ try {
+ Vector3D actual = (Vector3D)vector3DFormat.parseObject(source);
+ assertEquals(Vector3D.NEGATIVE_INFINITY, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testConstructorSingleFormat() {
+ NumberFormat nf = NumberFormat.getInstance();
+ Vector3DFormat cf = new Vector3DFormat(nf);
+ assertNotNull(cf);
+ assertEquals(nf, cf.getFormat());
+ }
+
+ public void testFormatObject() {
+ try {
+ CompositeFormat cf = new Vector3DFormat();
+ Object object = new Object();
+ cf.format(object);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testForgottenPrefix() {
+ ParsePosition pos = new ParsePosition(0);
+ assertNull(new Vector3DFormat().parse("1; 1; 1}", pos));
+ assertEquals(0, pos.getErrorIndex());
+ }
+
+ public void testForgottenSeparator() {
+ ParsePosition pos = new ParsePosition(0);
+ assertNull(new Vector3DFormat().parse("{1; 1 1}", pos));
+ assertEquals(6, pos.getErrorIndex());
+ }
+
+ public void testForgottenSuffix() {
+ ParsePosition pos = new ParsePosition(0);
+ assertNull(new Vector3DFormat().parse("{1; 1; 1 ", pos));
+ assertEquals(8, pos.getErrorIndex());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/Vector3DFormatTest.java b/src/test/java/org/apache/commons/math/geometry/Vector3DFormatTest.java
new file mode 100644
index 0000000..ca3f287
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/Vector3DFormatTest.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.geometry;
+
+import java.util.Locale;
+
+
+public class Vector3DFormatTest extends Vector3DFormatAbstractTest {
+
+ @Override
+ protected char getDecimalCharacter() {
+ return '.';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/geometry/Vector3DTest.java b/src/test/java/org/apache/commons/math/geometry/Vector3DTest.java
new file mode 100644
index 0000000..286eae6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/geometry/Vector3DTest.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.geometry;
+
+import org.apache.commons.math.geometry.Vector3D;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class Vector3DTest
+ extends TestCase {
+
+ public Vector3DTest(String name) {
+ super(name);
+ }
+
+ public void testConstructors() {
+ double r = FastMath.sqrt(2) /2;
+ checkVector(new Vector3D(2, new Vector3D(FastMath.PI / 3, -FastMath.PI / 4)),
+ r, r * FastMath.sqrt(3), -2 * r);
+ checkVector(new Vector3D(2, Vector3D.PLUS_I,
+ -3, Vector3D.MINUS_K),
+ 2, 0, 3);
+ checkVector(new Vector3D(2, Vector3D.PLUS_I,
+ 5, Vector3D.PLUS_J,
+ -3, Vector3D.MINUS_K),
+ 2, 5, 3);
+ checkVector(new Vector3D(2, Vector3D.PLUS_I,
+ 5, Vector3D.PLUS_J,
+ 5, Vector3D.MINUS_J,
+ -3, Vector3D.MINUS_K),
+ 2, 0, 3);
+ }
+
+ public void testCoordinates() {
+ Vector3D v = new Vector3D(1, 2, 3);
+ assertTrue(FastMath.abs(v.getX() - 1) < 1.0e-12);
+ assertTrue(FastMath.abs(v.getY() - 2) < 1.0e-12);
+ assertTrue(FastMath.abs(v.getZ() - 3) < 1.0e-12);
+ }
+
+ public void testNorm1() {
+ assertEquals(0.0, Vector3D.ZERO.getNorm1());
+ assertEquals(6.0, new Vector3D(1, -2, 3).getNorm1(), 0);
+ }
+
+ public void testNorm() {
+ assertEquals(0.0, Vector3D.ZERO.getNorm());
+ assertEquals(FastMath.sqrt(14), new Vector3D(1, 2, 3).getNorm(), 1.0e-12);
+ }
+
+ public void testNormInf() {
+ assertEquals(0.0, Vector3D.ZERO.getNormInf());
+ assertEquals(3.0, new Vector3D(1, -2, 3).getNormInf(), 0);
+ }
+
+ public void testDistance1() {
+ Vector3D v1 = new Vector3D(1, -2, 3);
+ Vector3D v2 = new Vector3D(-4, 2, 0);
+ assertEquals(0.0, Vector3D.distance1(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
+ assertEquals(12.0, Vector3D.distance1(v1, v2), 1.0e-12);
+ assertEquals(v1.subtract(v2).getNorm1(), Vector3D.distance1(v1, v2), 1.0e-12);
+ }
+
+ public void testDistance() {
+ Vector3D v1 = new Vector3D(1, -2, 3);
+ Vector3D v2 = new Vector3D(-4, 2, 0);
+ assertEquals(0.0, Vector3D.distance(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
+ assertEquals(FastMath.sqrt(50), Vector3D.distance(v1, v2), 1.0e-12);
+ assertEquals(v1.subtract(v2).getNorm(), Vector3D.distance(v1, v2), 1.0e-12);
+ }
+
+ public void testDistanceSq() {
+ Vector3D v1 = new Vector3D(1, -2, 3);
+ Vector3D v2 = new Vector3D(-4, 2, 0);
+ assertEquals(0.0, Vector3D.distanceSq(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
+ assertEquals(50.0, Vector3D.distanceSq(v1, v2), 1.0e-12);
+ assertEquals(Vector3D.distance(v1, v2) * Vector3D.distance(v1, v2),
+ Vector3D.distanceSq(v1, v2), 1.0e-12);
+ }
+
+ public void testDistanceInf() {
+ Vector3D v1 = new Vector3D(1, -2, 3);
+ Vector3D v2 = new Vector3D(-4, 2, 0);
+ assertEquals(0.0, Vector3D.distanceInf(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
+ assertEquals(5.0, Vector3D.distanceInf(v1, v2), 1.0e-12);
+ assertEquals(v1.subtract(v2).getNormInf(), Vector3D.distanceInf(v1, v2), 1.0e-12);
+ }
+
+ public void testSubtract() {
+
+ Vector3D v1 = new Vector3D(1, 2, 3);
+ Vector3D v2 = new Vector3D(-3, -2, -1);
+ v1 = v1.subtract(v2);
+ checkVector(v1, 4, 4, 4);
+
+ checkVector(v2.subtract(v1), -7, -6, -5);
+ checkVector(v2.subtract(3, v1), -15, -14, -13);
+
+ }
+
+ public void testAdd() {
+ Vector3D v1 = new Vector3D(1, 2, 3);
+ Vector3D v2 = new Vector3D(-3, -2, -1);
+ v1 = v1.add(v2);
+ checkVector(v1, -2, 0, 2);
+
+ checkVector(v2.add(v1), -5, -2, 1);
+ checkVector(v2.add(3, v1), -9, -2, 5);
+
+ }
+
+ public void testScalarProduct() {
+ Vector3D v = new Vector3D(1, 2, 3);
+ v = v.scalarMultiply(3);
+ checkVector(v, 3, 6, 9);
+
+ checkVector(v.scalarMultiply(0.5), 1.5, 3, 4.5);
+
+ }
+
+ public void testVectorialProducts() {
+ Vector3D v1 = new Vector3D(2, 1, -4);
+ Vector3D v2 = new Vector3D(3, 1, -1);
+
+ assertTrue(FastMath.abs(Vector3D.dotProduct(v1, v2) - 11) < 1.0e-12);
+
+ Vector3D v3 = Vector3D.crossProduct(v1, v2);
+ checkVector(v3, 3, -10, -1);
+
+ assertTrue(FastMath.abs(Vector3D.dotProduct(v1, v3)) < 1.0e-12);
+ assertTrue(FastMath.abs(Vector3D.dotProduct(v2, v3)) < 1.0e-12);
+
+ }
+
+ public void testAngular() {
+
+ assertEquals(0, Vector3D.PLUS_I.getAlpha(), 1.0e-10);
+ assertEquals(0, Vector3D.PLUS_I.getDelta(), 1.0e-10);
+ assertEquals(FastMath.PI / 2, Vector3D.PLUS_J.getAlpha(), 1.0e-10);
+ assertEquals(0, Vector3D.PLUS_J.getDelta(), 1.0e-10);
+ assertEquals(0, Vector3D.PLUS_K.getAlpha(), 1.0e-10);
+ assertEquals(FastMath.PI / 2, Vector3D.PLUS_K.getDelta(), 1.0e-10);
+
+ Vector3D u = new Vector3D(-1, 1, -1);
+ assertEquals(3 * FastMath.PI /4, u.getAlpha(), 1.0e-10);
+ assertEquals(-1.0 / FastMath.sqrt(3), FastMath.sin(u.getDelta()), 1.0e-10);
+
+ }
+
+ public void testAngularSeparation() {
+ Vector3D v1 = new Vector3D(2, -1, 4);
+
+ Vector3D k = v1.normalize();
+ Vector3D i = k.orthogonal();
+ Vector3D v2 = k.scalarMultiply(FastMath.cos(1.2)).add(i.scalarMultiply(FastMath.sin(1.2)));
+
+ assertTrue(FastMath.abs(Vector3D.angle(v1, v2) - 1.2) < 1.0e-12);
+
+ }
+
+ public void testNormalize() {
+ assertEquals(1.0, new Vector3D(5, -4, 2).normalize().getNorm(), 1.0e-12);
+ try {
+ Vector3D.ZERO.normalize();
+ fail("an exception should have been thrown");
+ } catch (ArithmeticException ae) {
+ // expected behavior
+ }
+ }
+
+ public void testOrthogonal() {
+ Vector3D v1 = new Vector3D(0.1, 2.5, 1.3);
+ assertEquals(0.0, Vector3D.dotProduct(v1, v1.orthogonal()), 1.0e-12);
+ Vector3D v2 = new Vector3D(2.3, -0.003, 7.6);
+ assertEquals(0.0, Vector3D.dotProduct(v2, v2.orthogonal()), 1.0e-12);
+ Vector3D v3 = new Vector3D(-1.7, 1.4, 0.2);
+ assertEquals(0.0, Vector3D.dotProduct(v3, v3.orthogonal()), 1.0e-12);
+ try {
+ new Vector3D(0, 0, 0).orthogonal();
+ fail("an exception should have been thrown");
+ } catch (ArithmeticException ae) {
+ // expected behavior
+ }
+ }
+
+ public void testAngle() {
+ assertEquals(0.22572612855273393616,
+ Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(4, 5, 6)),
+ 1.0e-12);
+ assertEquals(7.98595620686106654517199e-8,
+ Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(2, 4, 6.000001)),
+ 1.0e-12);
+ assertEquals(3.14159257373023116985197793156,
+ Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(-2, -4, -6.000001)),
+ 1.0e-12);
+ try {
+ Vector3D.angle(Vector3D.ZERO, Vector3D.PLUS_I);
+ fail("an exception should have been thrown");
+ } catch (ArithmeticException ae) {
+ // expected behavior
+ }
+ }
+
+ private void checkVector(Vector3D v, double x, double y, double z) {
+ assertEquals(x, v.getX(), 1.0e-12);
+ assertEquals(y, v.getY(), 1.0e-12);
+ assertEquals(z, v.getZ(), 1.0e-12);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/AbstractRealVectorTest.java b/src/test/java/org/apache/commons/math/linear/AbstractRealVectorTest.java
new file mode 100644
index 0000000..dfae89a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/AbstractRealVectorTest.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.linear;
+
+import junit.framework.TestCase;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.linear.RealVector.Entry;
+import org.apache.commons.math.util.FastMath;
+
+import java.util.Iterator;
+import java.util.Random;
+
+/**
+ *
+ */
+public class AbstractRealVectorTest extends TestCase {
+ private double[] vec1 = { 1d, 2d, 3d, 4d, 5d };
+ private double[] vec2 = { -3d, 0d, 0d, 2d, 1d };
+
+ private static class TestVectorImpl extends AbstractRealVector {
+ private double[] values;
+
+ TestVectorImpl(double[] values) {
+ this.values = values;
+ }
+
+ @Override
+ public double[] getData() { return values; }
+
+ @Override
+ public AbstractRealVector copy() {
+ return new TestVectorImpl(values.clone());
+ }
+
+
+ UnsupportedOperationException unsupported() {
+ return new UnsupportedOperationException("Test implementation only supports methods necessary for testing");
+ }
+
+ @Override
+ public RealVector add(RealVector v) throws IllegalArgumentException {
+ RealVector result = new ArrayRealVector(v);
+ return result.add(this);
+ }
+
+ @Override
+ public RealVector subtract(RealVector v) throws IllegalArgumentException {
+ RealVector result = new ArrayRealVector(v);
+ return result.subtract(this).mapMultiplyToSelf(-1);
+ }
+
+ @Override
+ public RealVector mapAddToSelf(double d) {
+ for(int i=0; i<values.length; i++) {
+ values[i] += d;
+ }
+ return this;
+ }
+
+ @Override
+ public RealVector mapSubtractToSelf(double d) {
+ for(int i=0; i<values.length; i++) {
+ values[i] -= d;
+ }
+ return this;
+ }
+
+ @Override
+ public RealVector mapMultiplyToSelf(double d) {
+ for(int i=0; i<values.length; i++) {
+ values[i] *= d;
+ }
+ return this;
+ }
+
+ @Override
+ public RealVector mapDivideToSelf(double d) {
+ for(int i=0; i<values.length; i++) {
+ values[i] /= d;
+ }
+ return this;
+ }
+
+ @Override
+ public RealVector mapPowToSelf(double d) {
+ for(int i=0; i<values.length; i++) {
+ values[i] = FastMath.pow(values[i], d);
+ }
+ return this;
+ }
+
+ @Override
+ public RealVector mapInvToSelf() {
+ for(int i=0; i<values.length; i++) {
+ values[i] = 1/values[i];
+ }
+ return this;
+ }
+
+ public RealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector ebeDivide(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double dotProduct(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getNorm() {
+ throw unsupported();
+ }
+
+ @Override
+ public double getL1Norm() {
+ throw unsupported();
+ }
+
+ @Override
+ public double getLInfNorm() {
+ throw unsupported();
+ }
+
+ public RealVector projection(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getEntry(int index) throws MatrixIndexException {
+ return values[index];
+ }
+
+ public void setEntry(int index, double value) throws MatrixIndexException {
+ values[index] = value;
+ }
+
+ public int getDimension() {
+ return values.length;
+ }
+
+ public RealVector append(RealVector v) {
+ throw unsupported();
+ }
+
+ public RealVector append(double d) {
+ throw unsupported();
+ }
+
+ public RealVector append(double[] a) {
+ throw unsupported();
+ }
+
+ public RealVector getSubVector(int index, int n) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public boolean isNaN() {
+ throw unsupported();
+ }
+
+ public boolean isInfinite() {
+ throw unsupported();
+ }
+ }
+
+ private static void assertEquals(double[] d1, double[] d2) {
+ assertEquals(d1.length, d2.length);
+ for(int i=0; i<d1.length; i++) assertEquals(d1[i], d2[i]);
+ }
+
+ public void testMap() throws Exception {
+ double[] vec1Squared = { 1d, 4d, 9d, 16d, 25d };
+ RealVector v = new TestVectorImpl(vec1.clone());
+ RealVector w = v.map(new UnivariateRealFunction() { public double value(double x) { return x * x; } });
+ assertEquals(vec1Squared, w.getData());
+ }
+
+ public void testIterator() throws Exception {
+ RealVector v = new TestVectorImpl(vec2.clone());
+ Entry e;
+ int i = 0;
+ for(Iterator<Entry> it = v.iterator(); it.hasNext() && (e = it.next()) != null; i++) {
+ assertEquals(vec2[i], e.getValue());
+ }
+ }
+
+ public void testSparseIterator() throws Exception {
+ RealVector v = new TestVectorImpl(vec2.clone());
+ Entry e;
+ int i = 0;
+ double[] nonDefaultV2 = { -3d, 2d, 1d };
+ for(Iterator<Entry> it = v.sparseIterator(); it.hasNext() && (e = it.next()) != null; i++) {
+ assertEquals(nonDefaultV2[i], e.getValue());
+ }
+ double [] onlyOne = {0d, 1.0, 0d};
+ v = new TestVectorImpl(onlyOne);
+ for(Iterator<Entry> it = v.sparseIterator(); it.hasNext() && (e = it.next()) != null; ) {
+ assertEquals(onlyOne[1], e.getValue());
+ }
+
+ }
+
+ public void testClone() throws Exception {
+ double[] d = new double[1000000];
+ Random r = new Random(1234);
+ for(int i=0;i<d.length; i++) d[i] = r.nextDouble();
+ assertTrue(new ArrayRealVector(d).getNorm() > 0);
+ double[] c = d.clone();
+ c[0] = 1;
+ assertNotSame(c[0], d[0]);
+ d[0] = 1;
+ assertEquals(new ArrayRealVector(d).getNorm(), new ArrayRealVector(c).getNorm());
+ long cloneTime = 0;
+ long setAndAddTime = 0;
+ for(int i=0; i<10; i++) {
+ long start = System.nanoTime();
+ double[] v = d.clone();
+ for(int j=0; j<v.length; j++) v[j] += 1234.5678;
+ if(i > 4) cloneTime += System.nanoTime() - start;
+ start = System.nanoTime();
+ v = new double[d.length];
+ for(int j=0; j<v.length; j++) v[j] = d[j] + 1234.5678;
+ if(i > 4) setAndAddTime += System.nanoTime() - start;
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java b/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java
new file mode 100644
index 0000000..3c50a6e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/Array2DRowRealMatrixTest.java
@@ -0,0 +1,1008 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link Array2DRowRealMatrix} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+
+public final class Array2DRowRealMatrixTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected double[][] id = { {1d,0d,0d}, {0d,1d,0d}, {0d,0d,1d} };
+
+ // Test data for group operations
+ protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
+ protected double[][] testDataLU = {{2d, 5d, 3d}, {.5d, -2.5d, 6.5d}, {0.5d, 0.2d, .2d}};
+ protected double[][] testDataPlus2 = { {3d,4d,5d}, {4d,7d,5d}, {3d,2d,10d} };
+ protected double[][] testDataMinus = { {-1d,-2d,-3d}, {-2d,-5d,-3d},
+ {-1d,0d,-8d} };
+ protected double[] testDataRow1 = {1d,2d,3d};
+ protected double[] testDataCol3 = {3d,3d,8d};
+ protected double[][] testDataInv =
+ { {-40d,16d,9d}, {13d,-5d,-3d}, {5d,-2d,-1d} };
+ protected double[] preMultTest = {8,12,33};
+ protected double[][] testData2 ={ {1d,2d,3d}, {2d,5d,3d}};
+ protected double[][] testData2T = { {1d,2d}, {2d,5d}, {3d,3d}};
+ protected double[][] testDataPlusInv =
+ { {-39d,18d,12d}, {15d,0d,0d}, {6d,-2d,7d} };
+
+ // lu decomposition tests
+ protected double[][] luData = { {2d,3d,3d}, {0d,5d,7d}, {6d,9d,8d} };
+ protected double[][] luDataLUDecomposition = { {6d,9d,8d}, {0d,5d,7d},
+ {0.33333333333333,0d,0.33333333333333} };
+
+ // singular matrices
+ protected double[][] singular = { {2d,3d}, {2d,3d} };
+ protected double[][] bigSingular = {{1d,2d,3d,4d}, {2d,5d,3d,4d},
+ {7d,3d,256d,1930d}, {3d,7d,6d,8d}}; // 4th row = 1st + 2nd
+ protected double[][] detData = { {1d,2d,3d}, {4d,5d,6d}, {7d,8d,10d} };
+ protected double[][] detData2 = { {1d, 3d}, {2d, 4d}};
+
+ // vectors
+ protected double[] testVector = {1,2,3};
+ protected double[] testVector2 = {1,2,3,4};
+
+ // submatrix accessor tests
+ protected double[][] subTestData = {{1, 2, 3, 4}, {1.5, 2.5, 3.5, 4.5},
+ {2, 4, 6, 8}, {4, 5, 6, 7}};
+ // array selections
+ protected double[][] subRows02Cols13 = { {2, 4}, {4, 8}};
+ protected double[][] subRows03Cols12 = { {2, 3}, {5, 6}};
+ protected double[][] subRows03Cols123 = { {2, 3, 4} , {5, 6, 7}};
+ // effective permutations
+ protected double[][] subRows20Cols123 = { {4, 6, 8} , {2, 3, 4}};
+ protected double[][] subRows31Cols31 = {{7, 5}, {4.5, 2.5}};
+ // contiguous ranges
+ protected double[][] subRows01Cols23 = {{3,4} , {3.5, 4.5}};
+ protected double[][] subRows23Cols00 = {{2} , {4}};
+ protected double[][] subRows00Cols33 = {{4}};
+ // row matrices
+ protected double[][] subRow0 = {{1,2,3,4}};
+ protected double[][] subRow3 = {{4,5,6,7}};
+ // column matrices
+ protected double[][] subColumn1 = {{2}, {2.5}, {4}, {5}};
+ protected double[][] subColumn3 = {{4}, {4.5}, {8}, {7}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public Array2DRowRealMatrixTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ Array2DRowRealMatrix m1 = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(m1.getData());
+ assertEquals(m2,m1);
+ Array2DRowRealMatrix m3 = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m4 = new Array2DRowRealMatrix(m3.getData(), false);
+ assertEquals(m4,m3);
+ }
+
+ /** test add */
+ public void testAdd() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix mInv = new Array2DRowRealMatrix(testDataInv);
+ RealMatrix mPlusMInv = m.add(mInv);
+ double[][] sumEntries = mPlusMInv.getData();
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ testDataPlusInv[row][col],sumEntries[row][col],
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test norm */
+ public void testNorm() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testData2);
+ assertEquals("testData norm",14d,m.getNorm(),entryTolerance);
+ assertEquals("testData2 norm",7d,m2.getNorm(),entryTolerance);
+ }
+
+ /** test Frobenius norm */
+ public void testFrobeniusNorm() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testData2);
+ assertEquals("testData Frobenius norm", FastMath.sqrt(117.0), m.getFrobeniusNorm(), entryTolerance);
+ assertEquals("testData2 Frobenius norm", FastMath.sqrt(52.0), m2.getFrobeniusNorm(), entryTolerance);
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testDataInv);
+ TestUtils.assertEquals("m-n = m + -n",m.subtract(m2),
+ m2.scalarMultiply(-1d).add(m),entryTolerance);
+ try {
+ m.subtract(new Array2DRowRealMatrix(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix mInv = new Array2DRowRealMatrix(testDataInv);
+ Array2DRowRealMatrix identity = new Array2DRowRealMatrix(id);
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix(testData2);
+ TestUtils.assertEquals("inverse multiply",m.multiply(mInv),
+ identity,entryTolerance);
+ TestUtils.assertEquals("inverse multiply",mInv.multiply(m),
+ identity,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m.multiply(identity),
+ m,entryTolerance);
+ TestUtils.assertEquals("identity multiply",identity.multiply(mInv),
+ mInv,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m2.multiply(identity),
+ m2,entryTolerance);
+ try {
+ m.multiply(new Array2DRowRealMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ //Additional Test for Array2DRowRealMatrixTest.testMultiply
+
+ private double[][] d3 = new double[][] {{1,2,3,4},{5,6,7,8}};
+ private double[][] d4 = new double[][] {{1},{2},{3},{4}};
+ private double[][] d5 = new double[][] {{30},{70}};
+
+ public void testMultiply2() {
+ RealMatrix m3 = new Array2DRowRealMatrix(d3);
+ RealMatrix m4 = new Array2DRowRealMatrix(d4);
+ RealMatrix m5 = new Array2DRowRealMatrix(d5);
+ TestUtils.assertEquals("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ RealMatrix m = new Array2DRowRealMatrix(id);
+ assertEquals("identity trace",3d,m.getTrace(),entryTolerance);
+ m = new Array2DRowRealMatrix(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ RealMatrix m = new Array2DRowRealMatrix(testData);
+ TestUtils.assertEquals("scalar add",new Array2DRowRealMatrix(testDataPlus2),
+ m.scalarAdd(2d),entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ RealMatrix m = new Array2DRowRealMatrix(id);
+ TestUtils.assertEquals("identity operate", testVector,
+ m.operate(testVector), entryTolerance);
+ TestUtils.assertEquals("identity operate", testVector,
+ m.operate(new ArrayRealVector(testVector)).getData(), entryTolerance);
+ m = new Array2DRowRealMatrix(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ RealMatrix a = new Array2DRowRealMatrix(new double[][] {
+ { 1, 2 }, { 3, 4 }, { 5, 6 }
+ }, false);
+ double[] b = a.operate(new double[] { 1, 1 });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( 3.0, b[0], 1.0e-12);
+ assertEquals( 7.0, b[1], 1.0e-12);
+ assertEquals(11.0, b[2], 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ RealMatrix m = new Array2DRowRealMatrix(testData);
+ RealMatrix mIT = new LUDecompositionImpl(m).getSolver().getInverse().transpose();
+ RealMatrix mTI = new LUDecompositionImpl(m.transpose()).getSolver().getInverse();
+ TestUtils.assertEquals("inverse-transpose", mIT, mTI, normTolerance);
+ m = new Array2DRowRealMatrix(testData2);
+ RealMatrix mt = new Array2DRowRealMatrix(testData2T);
+ TestUtils.assertEquals("transpose",mt,m.transpose(),normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ RealMatrix m = new Array2DRowRealMatrix(testData);
+ TestUtils.assertEquals("premultiply", m.preMultiply(testVector),
+ preMultTest, normTolerance);
+ TestUtils.assertEquals("premultiply", m.preMultiply(new ArrayRealVector(testVector).getData()),
+ preMultTest, normTolerance);
+ m = new Array2DRowRealMatrix(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ RealMatrix m3 = new Array2DRowRealMatrix(d3);
+ RealMatrix m4 = new Array2DRowRealMatrix(d4);
+ RealMatrix m5 = new Array2DRowRealMatrix(d5);
+ TestUtils.assertEquals("m3*m4=m5", m4.preMultiply(m3), m5, entryTolerance);
+
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix mInv = new Array2DRowRealMatrix(testDataInv);
+ Array2DRowRealMatrix identity = new Array2DRowRealMatrix(id);
+ TestUtils.assertEquals("inverse multiply",m.preMultiply(mInv),
+ identity,entryTolerance);
+ TestUtils.assertEquals("inverse multiply",mInv.preMultiply(m),
+ identity,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m.preMultiply(identity),
+ m,entryTolerance);
+ TestUtils.assertEquals("identity multiply",identity.preMultiply(mInv),
+ mInv,entryTolerance);
+ try {
+ m.preMultiply(new Array2DRowRealMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ RealMatrix m = new Array2DRowRealMatrix(testData);
+ TestUtils.assertEquals("get row",m.getRow(0),testDataRow1,entryTolerance);
+ TestUtils.assertEquals("get col",m.getColumn(2),testDataCol3,entryTolerance);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ RealMatrix m = new Array2DRowRealMatrix(testData);
+ assertEquals("get entry",m.getEntry(0,1),2d,entryTolerance);
+ try {
+ m.getEntry(10, 4);
+ fail ("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ double[][] matrixData = { {1d,2d,3d}, {2d,5d,3d}};
+ RealMatrix m = new Array2DRowRealMatrix(matrixData);
+ // One more with three rows, two columns
+ double[][] matrixData2 = { {1d,2d}, {2d,5d}, {1d, 7d}};
+ RealMatrix n = new Array2DRowRealMatrix(matrixData2);
+ // Now multiply m by n
+ RealMatrix p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ RealMatrix pInverse = new LUDecompositionImpl(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ double[][] coefficientsData = {{2, 3, -2}, {-1, 7, 6}, {4, -3, -5}};
+ RealMatrix coefficients = new Array2DRowRealMatrix(coefficientsData);
+ double[] constants = {1, -2, 1};
+ double[] solution = new LUDecompositionImpl(coefficients).getSolver().solve(constants);
+ assertEquals(2 * solution[0] + 3 * solution[1] -2 * solution[2], constants[0], 1E-12);
+ assertEquals(-1 * solution[0] + 7 * solution[1] + 6 * solution[2], constants[1], 1E-12);
+ assertEquals(4 * solution[0] - 3 * solution[1] -5 * solution[2], constants[2], 1E-12);
+
+ }
+
+ // test submatrix accessors
+ public void testGetSubMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ checkGetSubMatrix(m, subRows23Cols00, 2 , 3 , 0, 0, false);
+ checkGetSubMatrix(m, subRows00Cols33, 0 , 0 , 3, 3, false);
+ checkGetSubMatrix(m, subRows01Cols23, 0 , 1 , 2, 3, false);
+ checkGetSubMatrix(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 }, false);
+ checkGetSubMatrix(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 }, false);
+ checkGetSubMatrix(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 }, false);
+ checkGetSubMatrix(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 }, false);
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4, true);
+ checkGetSubMatrix(m, null, -1, 1, 2, 2, true);
+ checkGetSubMatrix(m, null, 1, 0, 2, 2, true);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4, true);
+ checkGetSubMatrix(m, null, new int[] {}, new int[] { 0 }, true);
+ checkGetSubMatrix(m, null, new int[] { 0 }, new int[] { 4 }, true);
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn,
+ boolean mustFail) {
+ try {
+ RealMatrix sub = m.getSubMatrix(startRow, endRow, startColumn, endColumn);
+ assertEquals(new Array2DRowRealMatrix(reference), sub);
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns,
+ boolean mustFail) {
+ try {
+ RealMatrix sub = m.getSubMatrix(selectedRows, selectedColumns);
+ assertEquals(new Array2DRowRealMatrix(reference), sub);
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ public void testCopySubMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ checkCopy(m, subRows23Cols00, 2 , 3 , 0, 0, false);
+ checkCopy(m, subRows00Cols33, 0 , 0 , 3, 3, false);
+ checkCopy(m, subRows01Cols23, 0 , 1 , 2, 3, false);
+ checkCopy(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 }, false);
+ checkCopy(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 }, false);
+ checkCopy(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 }, false);
+ checkCopy(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 }, false);
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+
+ checkCopy(m, null, 1, 0, 2, 4, true);
+ checkCopy(m, null, -1, 1, 2, 2, true);
+ checkCopy(m, null, 1, 0, 2, 2, true);
+ checkCopy(m, null, 1, 0, 2, 4, true);
+ checkCopy(m, null, new int[] {}, new int[] { 0 }, true);
+ checkCopy(m, null, new int[] { 0 }, new int[] { 4 }, true);
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn,
+ boolean mustFail) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(startRow, endRow, startColumn, endColumn, sub);
+ assertEquals(new Array2DRowRealMatrix(reference), new Array2DRowRealMatrix(sub));
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns,
+ boolean mustFail) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(selectedRows, selectedColumns, sub);
+ assertEquals(new Array2DRowRealMatrix(reference), new Array2DRowRealMatrix(sub));
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetRowMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealMatrix mRow0 = new Array2DRowRealMatrix(subRow0);
+ RealMatrix mRow3 = new Array2DRowRealMatrix(subRow3);
+ assertEquals("Row0", mRow0,
+ m.getRowMatrix(0));
+ assertEquals("Row3", mRow3,
+ m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealMatrix mRow3 = new Array2DRowRealMatrix(subRow3);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowMatrix(0, mRow3);
+ assertEquals(mRow3, m.getRowMatrix(0));
+ try {
+ m.setRowMatrix(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealMatrix mColumn1 = new Array2DRowRealMatrix(subColumn1);
+ RealMatrix mColumn3 = new Array2DRowRealMatrix(subColumn3);
+ assertEquals("Column1", mColumn1,
+ m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3,
+ m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnMatrix() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealMatrix mColumn3 = new Array2DRowRealMatrix(subColumn3);
+ assertNotSame(mColumn3, m.getColumnMatrix(1));
+ m.setColumnMatrix(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnMatrix(1));
+ try {
+ m.setColumnMatrix(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowVector() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealVector mRow0 = new ArrayRealVector(subRow0[0]);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertEquals("Row0", mRow0, m.getRowVector(0));
+ assertEquals("Row3", mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowVector() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowVector(0, mRow3);
+ assertEquals(mRow3, m.getRowVector(0));
+ try {
+ m.setRowVector(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnVector() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealVector mColumn1 = columnToVector(subColumn1);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnVector(1));
+ assertEquals("Column3", mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnVector() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertNotSame(mColumn3, m.getColumnVector(1));
+ m.setColumnVector(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnVector(1));
+ try {
+ m.setColumnVector(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private RealVector columnToVector(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayRealVector(data, false);
+ }
+
+ public void testGetRow() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ checkArrays(subRow0[0], m.getRow(0));
+ checkArrays(subRow3[0], m.getRow(3));
+ try {
+ m.getRow(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRow(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRow() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ assertTrue(subRow3[0][0] != m.getRow(0)[0]);
+ m.setRow(0, subRow3[0]);
+ checkArrays(subRow3[0], m.getRow(0));
+ try {
+ m.setRow(-1, subRow3[0]);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRow(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumn() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ double[] mColumn1 = columnToArray(subColumn1);
+ double[] mColumn3 = columnToArray(subColumn3);
+ checkArrays(mColumn1, m.getColumn(1));
+ checkArrays(mColumn3, m.getColumn(3));
+ try {
+ m.getColumn(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumn(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumn() {
+ RealMatrix m = new Array2DRowRealMatrix(subTestData);
+ double[] mColumn3 = columnToArray(subColumn3);
+ assertTrue(mColumn3[0] != m.getColumn(1)[0]);
+ m.setColumn(1, mColumn3);
+ checkArrays(mColumn3, m.getColumn(1));
+ try {
+ m.setColumn(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumn(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private double[] columnToArray(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return data;
+ }
+
+ private void checkArrays(double[] expected, double[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ Array2DRowRealMatrix m1 = (Array2DRowRealMatrix) m.copy();
+ Array2DRowRealMatrix mt = (Array2DRowRealMatrix) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new Array2DRowRealMatrix(bigSingular)));
+ }
+
+ public void testToString() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ assertEquals("Array2DRowRealMatrix{{1.0,2.0,3.0},{2.0,5.0,3.0},{1.0,0.0,8.0}}",
+ m.toString());
+ m = new Array2DRowRealMatrix();
+ assertEquals("Array2DRowRealMatrix{}",
+ m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ m.setSubMatrix(detData2,1,1);
+ RealMatrix expected = MatrixUtils.createRealMatrix
+ (new double[][] {{1.0,2.0,3.0},{2.0,1.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2,0,0);
+ expected = MatrixUtils.createRealMatrix
+ (new double[][] {{1.0,3.0,3.0},{2.0,4.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2,0,0);
+ expected = MatrixUtils.createRealMatrix
+ (new double[][] {{3.0,4.0,5.0},{4.0,7.0,5.0},{3.0,2.0,10.0}});
+ assertEquals(expected, m);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData,1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData,-1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData,1,-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ Array2DRowRealMatrix m2 = new Array2DRowRealMatrix();
+ try {
+ m2.setSubMatrix(testData,0,1);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ try {
+ m2.setSubMatrix(testData,1,0);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new double[][] {{1}, {2, 3}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new double[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testWalk() throws MatrixVisitorException {
+ int rows = 150;
+ int columns = 75;
+
+ RealMatrix m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInRowOrder(new SetVisitor());
+ GetVisitor getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInRowOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInColumnOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInColumnOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ }
+
+ public void testSerial() {
+ Array2DRowRealMatrix m = new Array2DRowRealMatrix(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+
+ private static class SetVisitor extends DefaultRealMatrixChangingVisitor {
+ @Override
+ public double visit(int i, int j, double value) {
+ return i + j / 1024.0;
+ }
+ }
+
+ private static class GetVisitor extends DefaultRealMatrixPreservingVisitor {
+ private int count = 0;
+ @Override
+ public void visit(int i, int j, double value) {
+ ++count;
+ assertEquals(i + j / 1024.0, value, 0.0);
+ }
+ public int getCount() {
+ return count;
+ }
+ }
+
+ //--------------- -----------------Protected methods
+
+ /** extracts the l and u matrices from compact lu representation */
+ protected void splitLU(RealMatrix lu, double[][] lowerData, double[][] upperData) throws InvalidMatrixException {
+ if (!lu.isSquare() || lowerData.length != lowerData[0].length || upperData.length != upperData[0].length ||
+ lowerData.length != upperData.length
+ || lowerData.length != lu.getRowDimension()) {
+ throw new InvalidMatrixException("incorrect dimensions");
+ }
+ int n = lu.getRowDimension();
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (j < i) {
+ lowerData[i][j] = lu.getEntry(i, j);
+ upperData[i][j] = 0d;
+ } else if (i == j) {
+ lowerData[i][j] = 1d;
+ upperData[i][j] = lu.getEntry(i, j);
+ } else {
+ lowerData[i][j] = 0d;
+ upperData[i][j] = lu.getEntry(i, j);
+ }
+ }
+ }
+ }
+
+ /** Returns the result of applying the given row permutation to the matrix */
+ protected RealMatrix permuteRows(RealMatrix matrix, int[] permutation) {
+ if (!matrix.isSquare() || matrix.getRowDimension() != permutation.length) {
+ throw new IllegalArgumentException("dimension mismatch");
+ }
+ int n = matrix.getRowDimension();
+ int m = matrix.getColumnDimension();
+ double out[][] = new double[m][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ out[i][j] = matrix.getEntry(permutation[i], j);
+ }
+ }
+ return new Array2DRowRealMatrix(out);
+ }
+
+// /** Useful for debugging */
+// private void dumpMatrix(RealMatrix m) {
+// for (int i = 0; i < m.getRowDimension(); i++) {
+// String os = "";
+// for (int j = 0; j < m.getColumnDimension(); j++) {
+// os += m.getEntry(i, j) + " ";
+// }
+// System.out.println(os);
+// }
+// }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/ArrayFieldVectorTest.java b/src/test/java/org/apache/commons/math/linear/ArrayFieldVectorTest.java
new file mode 100644
index 0000000..729dbb1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/ArrayFieldVectorTest.java
@@ -0,0 +1,632 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.linear;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.FieldElement;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionField;
+
+/**
+ * Test cases for the {@link ArrayFieldVector} class.
+ *
+ * @version $Revision: 1003993 $ $Date: 2010-10-03 18:39:16 +0200 (dim. 03 oct. 2010) $
+ */
+public class ArrayFieldVectorTest extends TestCase {
+
+ //
+ protected Fraction[][] ma1 = {
+ {new Fraction(1), new Fraction(2), new Fraction(3)},
+ {new Fraction(4), new Fraction(5), new Fraction(6)},
+ {new Fraction(7), new Fraction(8), new Fraction(9)}
+ };
+ protected Fraction[] vec1 = {new Fraction(1), new Fraction(2), new Fraction(3)};
+ protected Fraction[] vec2 = {new Fraction(4), new Fraction(5), new Fraction(6)};
+ protected Fraction[] vec3 = {new Fraction(7), new Fraction(8), new Fraction(9)};
+ protected Fraction[] vec4 = { new Fraction(1), new Fraction(2), new Fraction(3),
+ new Fraction(4), new Fraction(5), new Fraction(6),
+ new Fraction(7), new Fraction(8), new Fraction(9)};
+ protected Fraction[] vec_null = {new Fraction(0), new Fraction(0), new Fraction(0)};
+ protected Fraction[] dvec1 = {new Fraction(1), new Fraction(2), new Fraction(3),
+ new Fraction(4), new Fraction(5), new Fraction(6),
+ new Fraction(7), new Fraction(8), new Fraction(9)};
+ protected Fraction[][] mat1 = {
+ {new Fraction(1), new Fraction(2), new Fraction(3)},
+ {new Fraction(4), new Fraction(5), new Fraction(6)},
+ {new Fraction(7), new Fraction(8), new Fraction(9)}
+ };
+
+ // Testclass to test the FieldVector<Fraction> interface
+ // only with enough content to support the test
+ public static class FieldVectorTestImpl<T extends FieldElement<T>>
+ implements FieldVector<T>, Serializable {
+
+ private static final long serialVersionUID = 3970959016014158539L;
+
+ private final Field<T> field;
+
+ /** Entries of the vector. */
+ protected T[] data;
+
+ /** 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);
+ }
+
+ public FieldVectorTestImpl(T[] d) {
+ field = d[0].getField();
+ data = d.clone();
+ }
+
+ public Field<T> getField() {
+ return field;
+ }
+
+ private UnsupportedOperationException unsupported() {
+ return new UnsupportedOperationException("Not supported, unneeded for test purposes");
+ }
+
+ public FieldVector<T> copy() {
+ throw unsupported();
+ }
+
+ public FieldVector<T> add(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> add(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> subtract(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> subtract(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapAdd(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapAddToSelf(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapSubtract(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapSubtractToSelf(T d) {
+ throw unsupported();
+ }
+
+ 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 FieldVectorTestImpl<T>(out);
+ }
+
+ public FieldVector<T> mapMultiplyToSelf(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapDivide(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapDivideToSelf(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapInv() {
+ throw unsupported();
+ }
+
+ public FieldVector<T> mapInvToSelf() {
+ throw unsupported();
+ }
+
+ public FieldVector<T> ebeMultiply(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> ebeMultiply(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> ebeDivide(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> ebeDivide(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public T[] getData() {
+ return data.clone();
+ }
+
+ public T dotProduct(FieldVector<T> v) throws IllegalArgumentException {
+ T dot = field.getZero();
+ for (int i = 0; i < data.length; i++) {
+ dot = dot.add(data[i].multiply(v.getEntry(i)));
+ }
+ return dot;
+ }
+
+ public T dotProduct(T[] v) throws IllegalArgumentException {
+ T dot = field.getZero();
+ for (int i = 0; i < data.length; i++) {
+ dot = dot.add(data[i].multiply(v[i]));
+ }
+ return dot;
+ }
+
+ public FieldVector<T> projection(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldVector<T> projection(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldMatrix<T> outerProduct(FieldVector<T> v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public FieldMatrix<T> outerProduct(T[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public T getEntry(int index) throws MatrixIndexException {
+ return data[index];
+ }
+
+ public int getDimension() {
+ return data.length;
+ }
+
+ public FieldVector<T> append(FieldVector<T> v) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> append(T d) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> append(T[] a) {
+ throw unsupported();
+ }
+
+ public FieldVector<T> getSubVector(int index, int n) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setEntry(int index, T value) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setSubVector(int index, FieldVector<T> v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setSubVector(int index, T[] v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void set(T value) {
+ throw unsupported();
+ }
+
+ public T[] toArray() {
+ throw unsupported();
+ }
+
+ }
+
+ public void testConstructors() {
+
+ ArrayFieldVector<Fraction> v0 = new ArrayFieldVector<Fraction>(FractionField.getInstance());
+ assertEquals(0, v0.getDimension());
+
+ ArrayFieldVector<Fraction> v1 = new ArrayFieldVector<Fraction>(FractionField.getInstance(), 7);
+ assertEquals(7, v1.getDimension());
+ assertEquals(new Fraction(0), v1.getEntry(6));
+
+ ArrayFieldVector<Fraction> v2 = new ArrayFieldVector<Fraction>(5, new Fraction(123, 100));
+ assertEquals(5, v2.getDimension());
+ assertEquals(new Fraction(123, 100), v2.getEntry(4));
+
+ ArrayFieldVector<Fraction> v3 = new ArrayFieldVector<Fraction>(vec1);
+ assertEquals(3, v3.getDimension());
+ assertEquals(new Fraction(2), v3.getEntry(1));
+
+ ArrayFieldVector<Fraction> v4 = new ArrayFieldVector<Fraction>(vec4, 3, 2);
+ assertEquals(2, v4.getDimension());
+ assertEquals(new Fraction(4), v4.getEntry(0));
+ try {
+ new ArrayFieldVector<Fraction>(vec4, 8, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ FieldVector<Fraction> v5_i = new ArrayFieldVector<Fraction>(dvec1);
+ assertEquals(9, v5_i.getDimension());
+ assertEquals(new Fraction(9), v5_i.getEntry(8));
+
+ ArrayFieldVector<Fraction> v5 = new ArrayFieldVector<Fraction>(dvec1);
+ assertEquals(9, v5.getDimension());
+ assertEquals(new Fraction(9), v5.getEntry(8));
+
+ ArrayFieldVector<Fraction> v6 = new ArrayFieldVector<Fraction>(dvec1, 3, 2);
+ assertEquals(2, v6.getDimension());
+ assertEquals(new Fraction(4), v6.getEntry(0));
+ try {
+ new ArrayFieldVector<Fraction>(dvec1, 8, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ ArrayFieldVector<Fraction> v7 = new ArrayFieldVector<Fraction>(v1);
+ assertEquals(7, v7.getDimension());
+ assertEquals(new Fraction(0), v7.getEntry(6));
+
+ FieldVectorTestImpl<Fraction> v7_i = new FieldVectorTestImpl<Fraction>(vec1);
+
+ ArrayFieldVector<Fraction> v7_2 = new ArrayFieldVector<Fraction>(v7_i);
+ assertEquals(3, v7_2.getDimension());
+ assertEquals(new Fraction(2), v7_2.getEntry(1));
+
+ ArrayFieldVector<Fraction> v8 = new ArrayFieldVector<Fraction>(v1, true);
+ assertEquals(7, v8.getDimension());
+ assertEquals(new Fraction(0), v8.getEntry(6));
+ assertNotSame("testData not same object ", v1.data, v8.data);
+
+ ArrayFieldVector<Fraction> v8_2 = new ArrayFieldVector<Fraction>(v1, false);
+ assertEquals(7, v8_2.getDimension());
+ assertEquals(new Fraction(0), v8_2.getEntry(6));
+ assertEquals(v1.data, v8_2.data);
+
+ ArrayFieldVector<Fraction> v9 = new ArrayFieldVector<Fraction>(v1, v3);
+ assertEquals(10, v9.getDimension());
+ assertEquals(new Fraction(1), v9.getEntry(7));
+
+ }
+
+ public void testDataInOut() {
+
+ ArrayFieldVector<Fraction> v1 = new ArrayFieldVector<Fraction>(vec1);
+ ArrayFieldVector<Fraction> v2 = new ArrayFieldVector<Fraction>(vec2);
+ ArrayFieldVector<Fraction> v4 = new ArrayFieldVector<Fraction>(vec4);
+ FieldVectorTestImpl<Fraction> v2_t = new FieldVectorTestImpl<Fraction>(vec2);
+
+ FieldVector<Fraction> v_append_1 = v1.append(v2);
+ assertEquals(6, v_append_1.getDimension());
+ assertEquals(new Fraction(4), v_append_1.getEntry(3));
+
+ FieldVector<Fraction> v_append_2 = v1.append(new Fraction(2));
+ assertEquals(4, v_append_2.getDimension());
+ assertEquals(new Fraction(2), v_append_2.getEntry(3));
+
+ FieldVector<Fraction> v_append_3 = v1.append(vec2);
+ assertEquals(6, v_append_3.getDimension());
+ assertEquals(new Fraction(4), v_append_3.getEntry(3));
+
+ FieldVector<Fraction> v_append_4 = v1.append(v2_t);
+ assertEquals(6, v_append_4.getDimension());
+ assertEquals(new Fraction(4), v_append_4.getEntry(3));
+
+ FieldVector<Fraction> v_copy = v1.copy();
+ assertEquals(3, v_copy.getDimension());
+ assertNotSame("testData not same object ", v1.data, v_copy.getData());
+
+ Fraction[] a_frac = v1.toArray();
+ assertEquals(3, a_frac.length);
+ assertNotSame("testData not same object ", v1.data, a_frac);
+
+
+// ArrayFieldVector<Fraction> vout4 = (ArrayFieldVector<Fraction>) v1.clone();
+// assertEquals(3, vout4.getDimension());
+// assertEquals(v1.data, vout4.data);
+
+
+ FieldVector<Fraction> vout5 = v4.getSubVector(3, 3);
+ assertEquals(3, vout5.getDimension());
+ assertEquals(new Fraction(5), vout5.getEntry(1));
+ try {
+ v4.getSubVector(3, 7);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayFieldVector<Fraction> v_set1 = (ArrayFieldVector<Fraction>) v1.copy();
+ v_set1.setEntry(1, new Fraction(11));
+ assertEquals(new Fraction(11), v_set1.getEntry(1));
+ try {
+ v_set1.setEntry(3, new Fraction(11));
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayFieldVector<Fraction> v_set2 = (ArrayFieldVector<Fraction>) v4.copy();
+ v_set2.set(3, v1);
+ assertEquals(new Fraction(1), v_set2.getEntry(3));
+ assertEquals(new Fraction(7), v_set2.getEntry(6));
+ try {
+ v_set2.set(7, v1);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayFieldVector<Fraction> v_set3 = (ArrayFieldVector<Fraction>) v1.copy();
+ v_set3.set(new Fraction(13));
+ assertEquals(new Fraction(13), v_set3.getEntry(2));
+
+ try {
+ v_set3.getEntry(23);
+ fail("ArrayIndexOutOfBoundsException expected");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // expected behavior
+ }
+
+ ArrayFieldVector<Fraction> v_set4 = (ArrayFieldVector<Fraction>) v4.copy();
+ v_set4.setSubVector(3, v2_t);
+ assertEquals(new Fraction(4), v_set4.getEntry(3));
+ assertEquals(new Fraction(7), v_set4.getEntry(6));
+ try {
+ v_set4.setSubVector(7, v2_t);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+
+ ArrayFieldVector<Fraction> vout10 = (ArrayFieldVector<Fraction>) v1.copy();
+ ArrayFieldVector<Fraction> vout10_2 = (ArrayFieldVector<Fraction>) v1.copy();
+ assertEquals(vout10, vout10_2);
+ vout10_2.setEntry(0, new Fraction(11, 10));
+ assertNotSame(vout10, vout10_2);
+
+ }
+
+ public void testMapFunctions() {
+ ArrayFieldVector<Fraction> v1 = new ArrayFieldVector<Fraction>(vec1);
+
+ //octave = v1 .+ 2.0
+ FieldVector<Fraction> v_mapAdd = v1.mapAdd(new Fraction(2));
+ Fraction[] result_mapAdd = {new Fraction(3), new Fraction(4), new Fraction(5)};
+ checkArray("compare vectors" ,result_mapAdd,v_mapAdd.getData());
+
+ //octave = v1 .+ 2.0
+ FieldVector<Fraction> v_mapAddToSelf = v1.copy();
+ v_mapAddToSelf.mapAddToSelf(new Fraction(2));
+ Fraction[] result_mapAddToSelf = {new Fraction(3), new Fraction(4), new Fraction(5)};
+ checkArray("compare vectors" ,result_mapAddToSelf,v_mapAddToSelf.getData());
+
+ //octave = v1 .- 2.0
+ FieldVector<Fraction> v_mapSubtract = v1.mapSubtract(new Fraction(2));
+ Fraction[] result_mapSubtract = {new Fraction(-1), new Fraction(0), new Fraction(1)};
+ checkArray("compare vectors" ,result_mapSubtract,v_mapSubtract.getData());
+
+ //octave = v1 .- 2.0
+ FieldVector<Fraction> v_mapSubtractToSelf = v1.copy();
+ v_mapSubtractToSelf.mapSubtractToSelf(new Fraction(2));
+ Fraction[] result_mapSubtractToSelf = {new Fraction(-1), new Fraction(0), new Fraction(1)};
+ checkArray("compare vectors" ,result_mapSubtractToSelf,v_mapSubtractToSelf.getData());
+
+ //octave = v1 .* 2.0
+ FieldVector<Fraction> v_mapMultiply = v1.mapMultiply(new Fraction(2));
+ Fraction[] result_mapMultiply = {new Fraction(2), new Fraction(4), new Fraction(6)};
+ checkArray("compare vectors" ,result_mapMultiply,v_mapMultiply.getData());
+
+ //octave = v1 .* 2.0
+ FieldVector<Fraction> v_mapMultiplyToSelf = v1.copy();
+ v_mapMultiplyToSelf.mapMultiplyToSelf(new Fraction(2));
+ Fraction[] result_mapMultiplyToSelf = {new Fraction(2), new Fraction(4), new Fraction(6)};
+ checkArray("compare vectors" ,result_mapMultiplyToSelf,v_mapMultiplyToSelf.getData());
+
+ //octave = v1 ./ 2.0
+ FieldVector<Fraction> v_mapDivide = v1.mapDivide(new Fraction(2));
+ Fraction[] result_mapDivide = {new Fraction(1, 2), new Fraction(1), new Fraction(3, 2)};
+ checkArray("compare vectors" ,result_mapDivide,v_mapDivide.getData());
+
+ //octave = v1 ./ 2.0
+ FieldVector<Fraction> v_mapDivideToSelf = v1.copy();
+ v_mapDivideToSelf.mapDivideToSelf(new Fraction(2));
+ Fraction[] result_mapDivideToSelf = {new Fraction(1, 2), new Fraction(1), new Fraction(3, 2)};
+ checkArray("compare vectors" ,result_mapDivideToSelf,v_mapDivideToSelf.getData());
+
+ //octave = v1 .^-1
+ FieldVector<Fraction> v_mapInv = v1.mapInv();
+ Fraction[] result_mapInv = {new Fraction(1),new Fraction(1, 2),new Fraction(1, 3)};
+ checkArray("compare vectors" ,result_mapInv,v_mapInv.getData());
+
+ //octave = v1 .^-1
+ FieldVector<Fraction> v_mapInvToSelf = v1.copy();
+ v_mapInvToSelf.mapInvToSelf();
+ Fraction[] result_mapInvToSelf = {new Fraction(1),new Fraction(1, 2),new Fraction(1, 3)};
+ checkArray("compare vectors" ,result_mapInvToSelf,v_mapInvToSelf.getData());
+
+ }
+
+ public void testBasicFunctions() {
+ ArrayFieldVector<Fraction> v1 = new ArrayFieldVector<Fraction>(vec1);
+ ArrayFieldVector<Fraction> v2 = new ArrayFieldVector<Fraction>(vec2);
+ new ArrayFieldVector<Fraction>(vec_null);
+
+ FieldVectorTestImpl<Fraction> v2_t = new FieldVectorTestImpl<Fraction>(vec2);
+
+ //octave = v1 + v2
+ ArrayFieldVector<Fraction> v_add = v1.add(v2);
+ Fraction[] result_add = {new Fraction(5), new Fraction(7), new Fraction(9)};
+ checkArray("compare vect" ,v_add.getData(),result_add);
+
+ FieldVectorTestImpl<Fraction> vt2 = new FieldVectorTestImpl<Fraction>(vec2);
+ FieldVector<Fraction> v_add_i = v1.add(vt2);
+ Fraction[] result_add_i = {new Fraction(5), new Fraction(7), new Fraction(9)};
+ checkArray("compare vect" ,v_add_i.getData(),result_add_i);
+
+ //octave = v1 - v2
+ ArrayFieldVector<Fraction> v_subtract = v1.subtract(v2);
+ Fraction[] result_subtract = {new Fraction(-3), new Fraction(-3), new Fraction(-3)};
+ checkArray("compare vect" ,v_subtract.getData(),result_subtract);
+
+ FieldVector<Fraction> v_subtract_i = v1.subtract(vt2);
+ Fraction[] result_subtract_i = {new Fraction(-3), new Fraction(-3), new Fraction(-3)};
+ checkArray("compare vect" ,v_subtract_i.getData(),result_subtract_i);
+
+ // octave v1 .* v2
+ ArrayFieldVector<Fraction> v_ebeMultiply = v1.ebeMultiply(v2);
+ Fraction[] result_ebeMultiply = {new Fraction(4), new Fraction(10), new Fraction(18)};
+ checkArray("compare vect" ,v_ebeMultiply.getData(),result_ebeMultiply);
+
+ FieldVector<Fraction> v_ebeMultiply_2 = v1.ebeMultiply(v2_t);
+ Fraction[] result_ebeMultiply_2 = {new Fraction(4), new Fraction(10), new Fraction(18)};
+ checkArray("compare vect" ,v_ebeMultiply_2.getData(),result_ebeMultiply_2);
+
+ // octave v1 ./ v2
+ ArrayFieldVector<Fraction> v_ebeDivide = v1.ebeDivide(v2);
+ Fraction[] result_ebeDivide = {new Fraction(1, 4), new Fraction(2, 5), new Fraction(1, 2)};
+ checkArray("compare vect" ,v_ebeDivide.getData(),result_ebeDivide);
+
+ FieldVector<Fraction> v_ebeDivide_2 = v1.ebeDivide(v2_t);
+ Fraction[] result_ebeDivide_2 = {new Fraction(1, 4), new Fraction(2, 5), new Fraction(1, 2)};
+ checkArray("compare vect" ,v_ebeDivide_2.getData(),result_ebeDivide_2);
+
+ // octave dot(v1,v2)
+ Fraction dot = v1.dotProduct(v2);
+ assertEquals("compare val ",new Fraction(32), dot);
+
+ // octave dot(v1,v2_t)
+ Fraction dot_2 = v1.dotProduct(v2_t);
+ assertEquals("compare val ",new Fraction(32), dot_2);
+
+ FieldMatrix<Fraction> m_outerProduct = v1.outerProduct(v2);
+ assertEquals("compare val ",new Fraction(4), m_outerProduct.getEntry(0,0));
+
+ FieldMatrix<Fraction> m_outerProduct_2 = v1.outerProduct(v2_t);
+ assertEquals("compare val ",new Fraction(4), m_outerProduct_2.getEntry(0,0));
+
+ ArrayFieldVector<Fraction> v_projection = v1.projection(v2);
+ Fraction[] result_projection = {new Fraction(128, 77), new Fraction(160, 77), new Fraction(192, 77)};
+ checkArray("compare vect", v_projection.getData(), result_projection);
+
+ FieldVector<Fraction> v_projection_2 = v1.projection(v2_t);
+ Fraction[] result_projection_2 = {new Fraction(128, 77), new Fraction(160, 77), new Fraction(192, 77)};
+ checkArray("compare vect", v_projection_2.getData(), result_projection_2);
+
+ }
+
+ public void testMisc() {
+ ArrayFieldVector<Fraction> v1 = new ArrayFieldVector<Fraction>(vec1);
+ ArrayFieldVector<Fraction> v4 = new ArrayFieldVector<Fraction>(vec4);
+ FieldVector<Fraction> v4_2 = new ArrayFieldVector<Fraction>(vec4);
+
+ String out1 = v1.toString();
+ assertTrue("some output ", out1.length()!=0);
+ /*
+ Fraction[] dout1 = v1.copyOut();
+ assertEquals(3, dout1.length);
+ assertNotSame("testData not same object ", v1.data, dout1);
+ */
+ try {
+ v1.checkVectorDimensions(2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ try {
+ v1.checkVectorDimensions(v4);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ try {
+ v1.checkVectorDimensions(v4_2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ }
+
+ public void testSerial() {
+ ArrayFieldVector<Fraction> v = new ArrayFieldVector<Fraction>(vec1);
+ assertEquals(v,TestUtils.serializeAndRecover(v));
+ }
+
+ public void testZeroVectors() {
+
+ // when the field is not specified, array cannot be empty
+ try {
+ new ArrayFieldVector<Fraction>(new Fraction[0]);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+ try {
+ new ArrayFieldVector<Fraction>(new Fraction[0], true);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+ try {
+ new ArrayFieldVector<Fraction>(new Fraction[0], false);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ // when the field is specified, array can be empty
+ assertEquals(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), new Fraction[0]).getDimension());
+ assertEquals(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), new Fraction[0], true).getDimension());
+ assertEquals(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), new Fraction[0], false).getDimension());
+
+ }
+
+ /** verifies that two vectors are equals */
+ protected void checkArray(String msg, Fraction[] m, Fraction[] n) {
+ if (m.length != n.length) {
+ fail("vectors have different lengths");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i],n[i]);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/ArrayRealVectorTest.java b/src/test/java/org/apache/commons/math/linear/ArrayRealVectorTest.java
new file mode 100644
index 0000000..66b8160
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/ArrayRealVectorTest.java
@@ -0,0 +1,1310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Iterator;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link ArrayRealVector} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class ArrayRealVectorTest extends TestCase {
+
+ //
+ protected double[][] ma1 = {{1d, 2d, 3d}, {4d, 5d, 6d}, {7d, 8d, 9d}};
+ protected double[] vec1 = {1d, 2d, 3d};
+ protected double[] vec2 = {4d, 5d, 6d};
+ protected double[] vec3 = {7d, 8d, 9d};
+ protected double[] vec4 = {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d};
+ protected double[] vec5 = { -4d, 0d, 3d, 1d, -6d, 3d};
+ protected double[] vec_null = {0d, 0d, 0d};
+ protected Double[] dvec1 = {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d};
+ protected double[][] mat1 = {{1d, 2d, 3d}, {4d, 5d, 6d},{ 7d, 8d, 9d}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ // Testclass to test the RealVector interface
+ // only with enough content to support the test
+ public static class RealVectorTestImpl implements RealVector, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 4715341047369582908L;
+
+ /** Entries of the vector. */
+ protected double data[];
+
+ public RealVectorTestImpl(double[] d) {
+ data = d.clone();
+ }
+
+ private MatrixVisitorException unsupported() {
+ return new MatrixVisitorException("Not supported, unneeded for test purposes", new Object[0]);
+ }
+
+ public RealVector map(UnivariateRealFunction function) throws MatrixVisitorException {
+ throw unsupported();
+ }
+
+ public RealVector mapToSelf(UnivariateRealFunction function) throws MatrixVisitorException {
+ throw unsupported();
+ }
+
+ public Iterator<Entry> iterator() {
+ return new Iterator<Entry>() {
+ int i = 0;
+ public boolean hasNext() {
+ return i<data.length;
+ }
+ public Entry next() {
+ final int j = i++;
+ Entry e = new Entry() {
+ @Override
+ public double getValue() {
+ return data[j];
+ }
+ @Override
+ public void setValue(double newValue) {
+ data[j] = newValue;
+ }
+ };
+ e.setIndex(j);
+ return e;
+ }
+ public void remove() { }
+ };
+ }
+
+ public Iterator<Entry> sparseIterator() {
+ return iterator();
+ }
+
+ public RealVector copy() {
+ throw unsupported();
+ }
+
+ public RealVector add(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector add(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector subtract(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector subtract(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector mapAdd(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapAddToSelf(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapSubtract(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapSubtractToSelf(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapMultiply(double d) {
+ double[] out = new double[data.length];
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i] * d;
+ }
+ return new ArrayRealVector(out);
+ }
+
+ public RealVector mapMultiplyToSelf(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapDivide(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapDivideToSelf(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapPow(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapPowToSelf(double d) {
+ throw unsupported();
+ }
+
+ public RealVector mapExp() {
+ throw unsupported();
+ }
+
+ public RealVector mapExpToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapExpm1() {
+ throw unsupported();
+ }
+
+ public RealVector mapExpm1ToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapLog() {
+ throw unsupported();
+ }
+
+ public RealVector mapLogToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapLog10() {
+ throw unsupported();
+ }
+
+ public RealVector mapLog10ToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapLog1p() {
+ throw unsupported();
+ }
+
+ public RealVector mapLog1pToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapCosh() {
+ throw unsupported();
+ }
+
+ public RealVector mapCoshToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapSinh() {
+ throw unsupported();
+ }
+
+ public RealVector mapSinhToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapTanh() {
+ throw unsupported();
+ }
+
+ public RealVector mapTanhToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapCos() {
+ throw unsupported();
+ }
+
+ public RealVector mapCosToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapSin() {
+ throw unsupported();
+ }
+
+ public RealVector mapSinToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapTan() {
+ throw unsupported();
+ }
+
+ public RealVector mapTanToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapAcos() {
+ throw unsupported();
+ }
+
+ public RealVector mapAcosToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapAsin() {
+ throw unsupported();
+ }
+
+ public RealVector mapAsinToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapAtan() {
+ throw unsupported();
+ }
+
+ public RealVector mapAtanToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapInv() {
+ throw unsupported();
+ }
+
+ public RealVector mapInvToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapAbs() {
+ throw unsupported();
+ }
+
+ public RealVector mapAbsToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapSqrt() {
+ throw unsupported();
+ }
+
+ public RealVector mapSqrtToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapCbrt() {
+ throw unsupported();
+ }
+
+ public RealVector mapCbrtToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapCeil() {
+ throw unsupported();
+ }
+
+ public RealVector mapCeilToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapFloor() {
+ throw unsupported();
+ }
+
+ public RealVector mapFloorToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapRint() {
+ throw unsupported();
+ }
+
+ public RealVector mapRintToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapSignum() {
+ throw unsupported();
+ }
+
+ public RealVector mapSignumToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector mapUlp() {
+ throw unsupported();
+ }
+
+ public RealVector mapUlpToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector ebeMultiply(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector ebeDivide(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector ebeDivide(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double[] getData() {
+ return data.clone();
+ }
+
+ public double dotProduct(RealVector v) throws IllegalArgumentException {
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * v.getEntry(i);
+ }
+ return dot;
+ }
+
+ public double dotProduct(double[] v) throws IllegalArgumentException {
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * v[i];
+ }
+ return dot;
+ }
+
+ public double getNorm() {
+ throw unsupported();
+ }
+
+ public double getL1Norm() {
+ throw unsupported();
+ }
+
+ public double getLInfNorm() {
+ throw unsupported();
+ }
+
+ public double getDistance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getDistance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getL1Distance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getL1Distance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getLInfDistance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getLInfDistance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector unitVector() {
+ throw unsupported();
+ }
+
+ public void unitize() {
+ throw unsupported();
+ }
+
+ public RealVector projection(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector projection(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealMatrix outerProduct(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getEntry(int index) throws MatrixIndexException {
+ return data[index];
+ }
+
+ public int getDimension() {
+ return data.length;
+ }
+
+ public RealVector append(RealVector v) {
+ throw unsupported();
+ }
+
+ public RealVector append(double d) {
+ throw unsupported();
+ }
+
+ public RealVector append(double[] a) {
+ throw unsupported();
+ }
+
+ public RealVector getSubVector(int index, int n) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setEntry(int index, double value) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setSubVector(int index, RealVector v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setSubVector(int index, double[] v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void set(double value) {
+ throw unsupported();
+ }
+
+ public double[] toArray() {
+ throw unsupported();
+ }
+
+ public boolean isNaN() {
+ throw unsupported();
+ }
+
+ public boolean isInfinite() {
+ throw unsupported();
+ }
+
+ }
+
+ public void testConstructors() {
+
+ ArrayRealVector v0 = new ArrayRealVector();
+ assertEquals("testData len", 0, v0.getDimension());
+
+ ArrayRealVector v1 = new ArrayRealVector(7);
+ assertEquals("testData len", 7, v1.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v1.getEntry(6));
+
+ ArrayRealVector v2 = new ArrayRealVector(5, 1.23);
+ assertEquals("testData len", 5, v2.getDimension());
+ assertEquals("testData is 1.23 ", 1.23, v2.getEntry(4));
+
+ ArrayRealVector v3 = new ArrayRealVector(vec1);
+ assertEquals("testData len", 3, v3.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v3.getEntry(1));
+
+ ArrayRealVector v3_bis = new ArrayRealVector(vec1, true);
+ assertEquals("testData len", 3, v3_bis.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v3_bis.getEntry(1));
+ assertNotSame(v3_bis.getDataRef(), vec1);
+ assertNotSame(v3_bis.getData(), vec1);
+
+ ArrayRealVector v3_ter = new ArrayRealVector(vec1, false);
+ assertEquals("testData len", 3, v3_ter.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v3_ter.getEntry(1));
+ assertSame(v3_ter.getDataRef(), vec1);
+ assertNotSame(v3_ter.getData(), vec1);
+
+ ArrayRealVector v4 = new ArrayRealVector(vec4, 3, 2);
+ assertEquals("testData len", 2, v4.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v4.getEntry(0));
+ try {
+ new ArrayRealVector(vec4, 8, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ RealVector v5_i = new ArrayRealVector(dvec1);
+ assertEquals("testData len", 9, v5_i.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v5_i.getEntry(8));
+
+ ArrayRealVector v5 = new ArrayRealVector(dvec1);
+ assertEquals("testData len", 9, v5.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v5.getEntry(8));
+
+ ArrayRealVector v6 = new ArrayRealVector(dvec1, 3, 2);
+ assertEquals("testData len", 2, v6.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v6.getEntry(0));
+ try {
+ new ArrayRealVector(dvec1, 8, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v7 = new ArrayRealVector(v1);
+ assertEquals("testData len", 7, v7.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v7.getEntry(6));
+
+ RealVectorTestImpl v7_i = new RealVectorTestImpl(vec1);
+
+ ArrayRealVector v7_2 = new ArrayRealVector(v7_i);
+ assertEquals("testData len", 3, v7_2.getDimension());
+ assertEquals("testData is 0.0 ", 2.0d, v7_2.getEntry(1));
+
+ ArrayRealVector v8 = new ArrayRealVector(v1, true);
+ assertEquals("testData len", 7, v8.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v8.getEntry(6));
+ assertNotSame("testData not same object ", v1.data, v8.data);
+
+ ArrayRealVector v8_2 = new ArrayRealVector(v1, false);
+ assertEquals("testData len", 7, v8_2.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v8_2.getEntry(6));
+ assertEquals("testData same object ", v1.data, v8_2.data);
+
+ ArrayRealVector v9 = new ArrayRealVector(v1, v3);
+ assertEquals("testData len", 10, v9.getDimension());
+ assertEquals("testData is 1.0 ", 1.0, v9.getEntry(7));
+
+ ArrayRealVector v10 = new ArrayRealVector(v2, new RealVectorTestImpl(vec3));
+ assertEquals("testData len", 8, v10.getDimension());
+ assertEquals("testData is 1.23 ", 1.23, v10.getEntry(4));
+ assertEquals("testData is 7.0 ", 7.0, v10.getEntry(5));
+
+ ArrayRealVector v11 = new ArrayRealVector(new RealVectorTestImpl(vec3), v2);
+ assertEquals("testData len", 8, v11.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v11.getEntry(2));
+ assertEquals("testData is 1.23 ", 1.23, v11.getEntry(3));
+
+ ArrayRealVector v12 = new ArrayRealVector(v2, vec3);
+ assertEquals("testData len", 8, v12.getDimension());
+ assertEquals("testData is 1.23 ", 1.23, v12.getEntry(4));
+ assertEquals("testData is 7.0 ", 7.0, v12.getEntry(5));
+
+ ArrayRealVector v13 = new ArrayRealVector(vec3, v2);
+ assertEquals("testData len", 8, v13.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v13.getEntry(2));
+ assertEquals("testData is 1.23 ", 1.23, v13.getEntry(3));
+
+ ArrayRealVector v14 = new ArrayRealVector(vec3, vec4);
+ assertEquals("testData len", 12, v14.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v14.getEntry(2));
+ assertEquals("testData is 1.0 ", 1.0, v14.getEntry(3));
+
+ }
+
+ public void testDataInOut() {
+
+ ArrayRealVector v1 = new ArrayRealVector(vec1);
+ ArrayRealVector v2 = new ArrayRealVector(vec2);
+ ArrayRealVector v4 = new ArrayRealVector(vec4);
+ RealVectorTestImpl v2_t = new RealVectorTestImpl(vec2);
+
+ RealVector v_append_1 = v1.append(v2);
+ assertEquals("testData len", 6, v_append_1.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v_append_1.getEntry(3));
+
+ RealVector v_append_2 = v1.append(2.0);
+ assertEquals("testData len", 4, v_append_2.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v_append_2.getEntry(3));
+
+ RealVector v_append_3 = v1.append(vec2);
+ assertEquals("testData len", 6, v_append_3.getDimension());
+ assertEquals("testData is ", 4.0, v_append_3.getEntry(3));
+
+ RealVector v_append_4 = v1.append(v2_t);
+ assertEquals("testData len", 6, v_append_4.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v_append_4.getEntry(3));
+
+ RealVector v_append_5 = v1.append((RealVector) v2);
+ assertEquals("testData len", 6, v_append_5.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v_append_5.getEntry(3));
+
+ RealVector v_copy = v1.copy();
+ assertEquals("testData len", 3, v_copy.getDimension());
+ assertNotSame("testData not same object ", v1.data, v_copy.getData());
+
+ double[] a_double = v1.toArray();
+ assertEquals("testData len", 3, a_double.length);
+ assertNotSame("testData not same object ", v1.data, a_double);
+
+
+// ArrayRealVector vout4 = (ArrayRealVector) v1.clone();
+// assertEquals("testData len", 3, vout4.getDimension());
+// assertEquals("testData not same object ", v1.data, vout4.data);
+
+
+ RealVector vout5 = v4.getSubVector(3, 3);
+ assertEquals("testData len", 3, vout5.getDimension());
+ assertEquals("testData is 4.0 ", 5.0, vout5.getEntry(1));
+ try {
+ v4.getSubVector(3, 7);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_set1 = (ArrayRealVector) v1.copy();
+ v_set1.setEntry(1, 11.0);
+ assertEquals("testData is 11.0 ", 11.0, v_set1.getEntry(1));
+ try {
+ v_set1.setEntry(3, 11.0);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_set2 = (ArrayRealVector) v4.copy();
+ v_set2.set(3, v1);
+ assertEquals("testData is 1.0 ", 1.0, v_set2.getEntry(3));
+ assertEquals("testData is 7.0 ", 7.0, v_set2.getEntry(6));
+ try {
+ v_set2.set(7, v1);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_set3 = (ArrayRealVector) v1.copy();
+ v_set3.set(13.0);
+ assertEquals("testData is 13.0 ", 13.0, v_set3.getEntry(2));
+
+ try {
+ v_set3.getEntry(23);
+ fail("ArrayIndexOutOfBoundsException expected");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_set4 = (ArrayRealVector) v4.copy();
+ v_set4.setSubVector(3, v2_t);
+ assertEquals("testData is 1.0 ", 4.0, v_set4.getEntry(3));
+ assertEquals("testData is 7.0 ", 7.0, v_set4.getEntry(6));
+ try {
+ v_set4.setSubVector(7, v2_t);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+
+ ArrayRealVector vout10 = (ArrayRealVector) v1.copy();
+ ArrayRealVector vout10_2 = (ArrayRealVector) v1.copy();
+ assertEquals(vout10, vout10_2);
+ vout10_2.setEntry(0, 1.1);
+ assertNotSame(vout10, vout10_2);
+
+ }
+
+ public void testMapFunctions() {
+ ArrayRealVector v1 = new ArrayRealVector(vec1);
+
+ //octave = v1 .+ 2.0
+ RealVector v_mapAdd = v1.mapAdd(2.0d);
+ double[] result_mapAdd = {3d, 4d, 5d};
+ assertClose("compare vectors" ,result_mapAdd,v_mapAdd.getData(),normTolerance);
+
+ //octave = v1 .+ 2.0
+ RealVector v_mapAddToSelf = v1.copy();
+ v_mapAddToSelf.mapAddToSelf(2.0d);
+ double[] result_mapAddToSelf = {3d, 4d, 5d};
+ assertClose("compare vectors" ,result_mapAddToSelf,v_mapAddToSelf.getData(),normTolerance);
+
+ //octave = v1 .- 2.0
+ RealVector v_mapSubtract = v1.mapSubtract(2.0d);
+ double[] result_mapSubtract = {-1d, 0d, 1d};
+ assertClose("compare vectors" ,result_mapSubtract,v_mapSubtract.getData(),normTolerance);
+
+ //octave = v1 .- 2.0
+ RealVector v_mapSubtractToSelf = v1.copy();
+ v_mapSubtractToSelf.mapSubtractToSelf(2.0d);
+ double[] result_mapSubtractToSelf = {-1d, 0d, 1d};
+ assertClose("compare vectors" ,result_mapSubtractToSelf,v_mapSubtractToSelf.getData(),normTolerance);
+
+ //octave = v1 .* 2.0
+ RealVector v_mapMultiply = v1.mapMultiply(2.0d);
+ double[] result_mapMultiply = {2d, 4d, 6d};
+ assertClose("compare vectors" ,result_mapMultiply,v_mapMultiply.getData(),normTolerance);
+
+ //octave = v1 .* 2.0
+ RealVector v_mapMultiplyToSelf = v1.copy();
+ v_mapMultiplyToSelf.mapMultiplyToSelf(2.0d);
+ double[] result_mapMultiplyToSelf = {2d, 4d, 6d};
+ assertClose("compare vectors" ,result_mapMultiplyToSelf,v_mapMultiplyToSelf.getData(),normTolerance);
+
+ //octave = v1 ./ 2.0
+ RealVector v_mapDivide = v1.mapDivide(2.0d);
+ double[] result_mapDivide = {.5d, 1d, 1.5d};
+ assertClose("compare vectors" ,result_mapDivide,v_mapDivide.getData(),normTolerance);
+
+ //octave = v1 ./ 2.0
+ RealVector v_mapDivideToSelf = v1.copy();
+ v_mapDivideToSelf.mapDivideToSelf(2.0d);
+ double[] result_mapDivideToSelf = {.5d, 1d, 1.5d};
+ assertClose("compare vectors" ,result_mapDivideToSelf,v_mapDivideToSelf.getData(),normTolerance);
+
+ //octave = v1 .^ 2.0
+ RealVector v_mapPow = v1.mapPow(2.0d);
+ double[] result_mapPow = {1d, 4d, 9d};
+ assertClose("compare vectors" ,result_mapPow,v_mapPow.getData(),normTolerance);
+
+ //octave = v1 .^ 2.0
+ RealVector v_mapPowToSelf = v1.copy();
+ v_mapPowToSelf.mapPowToSelf(2.0d);
+ double[] result_mapPowToSelf = {1d, 4d, 9d};
+ assertClose("compare vectors" ,result_mapPowToSelf,v_mapPowToSelf.getData(),normTolerance);
+
+ //octave = exp(v1)
+ RealVector v_mapExp = v1.mapExp();
+ double[] result_mapExp = {2.718281828459045e+00d,7.389056098930650e+00d, 2.008553692318767e+01d};
+ assertClose("compare vectors" ,result_mapExp,v_mapExp.getData(),normTolerance);
+
+ //octave = exp(v1)
+ RealVector v_mapExpToSelf = v1.copy();
+ v_mapExpToSelf.mapExpToSelf();
+ double[] result_mapExpToSelf = {2.718281828459045e+00d,7.389056098930650e+00d, 2.008553692318767e+01d};
+ assertClose("compare vectors" ,result_mapExpToSelf,v_mapExpToSelf.getData(),normTolerance);
+
+
+ //octave = ???
+ RealVector v_mapExpm1 = v1.mapExpm1();
+ double[] result_mapExpm1 = {1.718281828459045d,6.38905609893065d, 19.085536923187668d};
+ assertClose("compare vectors" ,result_mapExpm1,v_mapExpm1.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapExpm1ToSelf = v1.copy();
+ v_mapExpm1ToSelf.mapExpm1ToSelf();
+ double[] result_mapExpm1ToSelf = {1.718281828459045d,6.38905609893065d, 19.085536923187668d};
+ assertClose("compare vectors" ,result_mapExpm1ToSelf,v_mapExpm1ToSelf.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLog = v1.mapLog();
+ double[] result_mapLog = {0d,6.931471805599453e-01d, 1.098612288668110e+00d};
+ assertClose("compare vectors" ,result_mapLog,v_mapLog.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLogToSelf = v1.copy();
+ v_mapLogToSelf.mapLogToSelf();
+ double[] result_mapLogToSelf = {0d,6.931471805599453e-01d, 1.098612288668110e+00d};
+ assertClose("compare vectors" ,result_mapLogToSelf,v_mapLogToSelf.getData(),normTolerance);
+
+ //octave = log10(v1)
+ RealVector v_mapLog10 = v1.mapLog10();
+ double[] result_mapLog10 = {0d,3.010299956639812e-01d, 4.771212547196624e-01d};
+ assertClose("compare vectors" ,result_mapLog10,v_mapLog10.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLog10ToSelf = v1.copy();
+ v_mapLog10ToSelf.mapLog10ToSelf();
+ double[] result_mapLog10ToSelf = {0d,3.010299956639812e-01d, 4.771212547196624e-01d};
+ assertClose("compare vectors" ,result_mapLog10ToSelf,v_mapLog10ToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapLog1p = v1.mapLog1p();
+ double[] result_mapLog1p = {0.6931471805599453d,1.0986122886681096d,1.3862943611198906d};
+ assertClose("compare vectors" ,result_mapLog1p,v_mapLog1p.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapLog1pToSelf = v1.copy();
+ v_mapLog1pToSelf.mapLog1pToSelf();
+ double[] result_mapLog1pToSelf = {0.6931471805599453d,1.0986122886681096d,1.3862943611198906d};
+ assertClose("compare vectors" ,result_mapLog1pToSelf,v_mapLog1pToSelf.getData(),normTolerance);
+
+ //octave = cosh(v1)
+ RealVector v_mapCosh = v1.mapCosh();
+ double[] result_mapCosh = {1.543080634815244e+00d,3.762195691083631e+00d, 1.006766199577777e+01d};
+ assertClose("compare vectors" ,result_mapCosh,v_mapCosh.getData(),normTolerance);
+
+ //octave = cosh(v1)
+ RealVector v_mapCoshToSelf = v1.copy();
+ v_mapCoshToSelf.mapCoshToSelf();
+ double[] result_mapCoshToSelf = {1.543080634815244e+00d,3.762195691083631e+00d, 1.006766199577777e+01d};
+ assertClose("compare vectors" ,result_mapCoshToSelf,v_mapCoshToSelf.getData(),normTolerance);
+
+ //octave = sinh(v1)
+ RealVector v_mapSinh = v1.mapSinh();
+ double[] result_mapSinh = {1.175201193643801e+00d,3.626860407847019e+00d, 1.001787492740990e+01d};
+ assertClose("compare vectors" ,result_mapSinh,v_mapSinh.getData(),normTolerance);
+
+ //octave = sinh(v1)
+ RealVector v_mapSinhToSelf = v1.copy();
+ v_mapSinhToSelf.mapSinhToSelf();
+ double[] result_mapSinhToSelf = {1.175201193643801e+00d,3.626860407847019e+00d, 1.001787492740990e+01d};
+ assertClose("compare vectors" ,result_mapSinhToSelf,v_mapSinhToSelf.getData(),normTolerance);
+
+ //octave = tanh(v1)
+ RealVector v_mapTanh = v1.mapTanh();
+ double[] result_mapTanh = {7.615941559557649e-01d,9.640275800758169e-01d,9.950547536867305e-01d};
+ assertClose("compare vectors" ,result_mapTanh,v_mapTanh.getData(),normTolerance);
+
+ //octave = tanh(v1)
+ RealVector v_mapTanhToSelf = v1.copy();
+ v_mapTanhToSelf.mapTanhToSelf();
+ double[] result_mapTanhToSelf = {7.615941559557649e-01d,9.640275800758169e-01d,9.950547536867305e-01d};
+ assertClose("compare vectors" ,result_mapTanhToSelf,v_mapTanhToSelf.getData(),normTolerance);
+
+ //octave = cos(v1)
+ RealVector v_mapCos = v1.mapCos();
+ double[] result_mapCos = {5.403023058681398e-01d,-4.161468365471424e-01d, -9.899924966004454e-01d};
+ assertClose("compare vectors" ,result_mapCos,v_mapCos.getData(),normTolerance);
+
+ //octave = cos(v1)
+ RealVector v_mapCosToSelf = v1.copy();
+ v_mapCosToSelf.mapCosToSelf();
+ double[] result_mapCosToSelf = {5.403023058681398e-01d,-4.161468365471424e-01d, -9.899924966004454e-01d};
+ assertClose("compare vectors" ,result_mapCosToSelf,v_mapCosToSelf.getData(),normTolerance);
+
+ //octave = sin(v1)
+ RealVector v_mapSin = v1.mapSin();
+ double[] result_mapSin = {8.414709848078965e-01d,9.092974268256817e-01d,1.411200080598672e-01d};
+ assertClose("compare vectors" ,result_mapSin,v_mapSin.getData(),normTolerance);
+
+ //octave = sin(v1)
+ RealVector v_mapSinToSelf = v1.copy();
+ v_mapSinToSelf.mapSinToSelf();
+ double[] result_mapSinToSelf = {8.414709848078965e-01d,9.092974268256817e-01d,1.411200080598672e-01d};
+ assertClose("compare vectors" ,result_mapSinToSelf,v_mapSinToSelf.getData(),normTolerance);
+
+ //octave = tan(v1)
+ RealVector v_mapTan = v1.mapTan();
+ double[] result_mapTan = {1.557407724654902e+00d,-2.185039863261519e+00d,-1.425465430742778e-01d};
+ assertClose("compare vectors" ,result_mapTan,v_mapTan.getData(),normTolerance);
+
+ //octave = tan(v1)
+ RealVector v_mapTanToSelf = v1.copy();
+ v_mapTanToSelf.mapTanToSelf();
+ double[] result_mapTanToSelf = {1.557407724654902e+00d,-2.185039863261519e+00d,-1.425465430742778e-01d};
+ assertClose("compare vectors" ,result_mapTanToSelf,v_mapTanToSelf.getData(),normTolerance);
+
+ double[] vat_a = {0d, 0.5d, 1.0d};
+ ArrayRealVector vat = new ArrayRealVector(vat_a);
+
+ //octave = acos(vat)
+ RealVector v_mapAcos = vat.mapAcos();
+ double[] result_mapAcos = {1.570796326794897e+00d,1.047197551196598e+00d, 0.0d};
+ assertClose("compare vectors" ,result_mapAcos,v_mapAcos.getData(),normTolerance);
+
+ //octave = acos(vat)
+ RealVector v_mapAcosToSelf = vat.copy();
+ v_mapAcosToSelf.mapAcosToSelf();
+ double[] result_mapAcosToSelf = {1.570796326794897e+00d,1.047197551196598e+00d, 0.0d};
+ assertClose("compare vectors" ,result_mapAcosToSelf,v_mapAcosToSelf.getData(),normTolerance);
+
+ //octave = asin(vat)
+ RealVector v_mapAsin = vat.mapAsin();
+ double[] result_mapAsin = {0.0d,5.235987755982989e-01d,1.570796326794897e+00d};
+ assertClose("compare vectors" ,result_mapAsin,v_mapAsin.getData(),normTolerance);
+
+ //octave = asin(vat)
+ RealVector v_mapAsinToSelf = vat.copy();
+ v_mapAsinToSelf.mapAsinToSelf();
+ double[] result_mapAsinToSelf = {0.0d,5.235987755982989e-01d,1.570796326794897e+00d};
+ assertClose("compare vectors" ,result_mapAsinToSelf,v_mapAsinToSelf.getData(),normTolerance);
+
+ //octave = atan(vat)
+ RealVector v_mapAtan = vat.mapAtan();
+ double[] result_mapAtan = {0.0d,4.636476090008061e-01d,7.853981633974483e-01d};
+ assertClose("compare vectors" ,result_mapAtan,v_mapAtan.getData(),normTolerance);
+
+ //octave = atan(vat)
+ RealVector v_mapAtanToSelf = vat.copy();
+ v_mapAtanToSelf.mapAtanToSelf();
+ double[] result_mapAtanToSelf = {0.0d,4.636476090008061e-01d,7.853981633974483e-01d};
+ assertClose("compare vectors" ,result_mapAtanToSelf,v_mapAtanToSelf.getData(),normTolerance);
+
+ //octave = v1 .^-1
+ RealVector v_mapInv = v1.mapInv();
+ double[] result_mapInv = {1d,0.5d,3.333333333333333e-01d};
+ assertClose("compare vectors" ,result_mapInv,v_mapInv.getData(),normTolerance);
+
+ //octave = v1 .^-1
+ RealVector v_mapInvToSelf = v1.copy();
+ v_mapInvToSelf.mapInvToSelf();
+ double[] result_mapInvToSelf = {1d,0.5d,3.333333333333333e-01d};
+ assertClose("compare vectors" ,result_mapInvToSelf,v_mapInvToSelf.getData(),normTolerance);
+
+ double[] abs_a = {-1.0d, 0.0d, 1.0d};
+ ArrayRealVector abs_v = new ArrayRealVector(abs_a);
+
+ //octave = abs(abs_v)
+ RealVector v_mapAbs = abs_v.mapAbs();
+ double[] result_mapAbs = {1d,0d,1d};
+ assertClose("compare vectors" ,result_mapAbs,v_mapAbs.getData(),normTolerance);
+
+ //octave = abs(abs_v)
+ RealVector v_mapAbsToSelf = abs_v.copy();
+ v_mapAbsToSelf.mapAbsToSelf();
+ double[] result_mapAbsToSelf = {1d,0d,1d};
+ assertClose("compare vectors" ,result_mapAbsToSelf,v_mapAbsToSelf.getData(),normTolerance);
+
+ //octave = sqrt(v1)
+ RealVector v_mapSqrt = v1.mapSqrt();
+ double[] result_mapSqrt = {1d,1.414213562373095e+00d,1.732050807568877e+00d};
+ assertClose("compare vectors" ,result_mapSqrt,v_mapSqrt.getData(),normTolerance);
+
+ //octave = sqrt(v1)
+ RealVector v_mapSqrtToSelf = v1.copy();
+ v_mapSqrtToSelf.mapSqrtToSelf();
+ double[] result_mapSqrtToSelf = {1d,1.414213562373095e+00d,1.732050807568877e+00d};
+ assertClose("compare vectors" ,result_mapSqrtToSelf,v_mapSqrtToSelf.getData(),normTolerance);
+
+ double[] cbrt_a = {-2.0d, 0.0d, 2.0d};
+ ArrayRealVector cbrt_v = new ArrayRealVector(cbrt_a);
+
+ //octave = ???
+ RealVector v_mapCbrt = cbrt_v.mapCbrt();
+ double[] result_mapCbrt = {-1.2599210498948732d,0d,1.2599210498948732d};
+ assertClose("compare vectors" ,result_mapCbrt,v_mapCbrt.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapCbrtToSelf = cbrt_v.copy();
+ v_mapCbrtToSelf.mapCbrtToSelf();
+ double[] result_mapCbrtToSelf = {-1.2599210498948732d,0d,1.2599210498948732d};
+ assertClose("compare vectors" ,result_mapCbrtToSelf,v_mapCbrtToSelf.getData(),normTolerance);
+
+ double[] ceil_a = {-1.1d, 0.9d, 1.1d};
+ ArrayRealVector ceil_v = new ArrayRealVector(ceil_a);
+
+ //octave = ceil(ceil_v)
+ RealVector v_mapCeil = ceil_v.mapCeil();
+ double[] result_mapCeil = {-1d,1d,2d};
+ assertClose("compare vectors" ,result_mapCeil,v_mapCeil.getData(),normTolerance);
+
+ //octave = ceil(ceil_v)
+ RealVector v_mapCeilToSelf = ceil_v.copy();
+ v_mapCeilToSelf.mapCeilToSelf();
+ double[] result_mapCeilToSelf = {-1d,1d,2d};
+ assertClose("compare vectors" ,result_mapCeilToSelf,v_mapCeilToSelf.getData(),normTolerance);
+
+ //octave = floor(ceil_v)
+ RealVector v_mapFloor = ceil_v.mapFloor();
+ double[] result_mapFloor = {-2d,0d,1d};
+ assertClose("compare vectors" ,result_mapFloor,v_mapFloor.getData(),normTolerance);
+
+ //octave = floor(ceil_v)
+ RealVector v_mapFloorToSelf = ceil_v.copy();
+ v_mapFloorToSelf.mapFloorToSelf();
+ double[] result_mapFloorToSelf = {-2d,0d,1d};
+ assertClose("compare vectors" ,result_mapFloorToSelf,v_mapFloorToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapRint = ceil_v.mapRint();
+ double[] result_mapRint = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapRint,v_mapRint.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapRintToSelf = ceil_v.copy();
+ v_mapRintToSelf.mapRintToSelf();
+ double[] result_mapRintToSelf = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapRintToSelf,v_mapRintToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapSignum = ceil_v.mapSignum();
+ double[] result_mapSignum = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapSignum,v_mapSignum.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapSignumToSelf = ceil_v.copy();
+ v_mapSignumToSelf.mapSignumToSelf();
+ double[] result_mapSignumToSelf = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapSignumToSelf,v_mapSignumToSelf.getData(),normTolerance);
+
+
+ // Is with the used resolutions of limited value as test
+ //octave = ???
+ RealVector v_mapUlp = ceil_v.mapUlp();
+ double[] result_mapUlp = {2.220446049250313E-16d,1.1102230246251565E-16d,2.220446049250313E-16d};
+ assertClose("compare vectors" ,result_mapUlp,v_mapUlp.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapUlpToSelf = ceil_v.copy();
+ v_mapUlpToSelf.mapUlpToSelf();
+ double[] result_mapUlpToSelf = {2.220446049250313E-16d,1.1102230246251565E-16d,2.220446049250313E-16d};
+ assertClose("compare vectors" ,result_mapUlpToSelf,v_mapUlpToSelf.getData(),normTolerance);
+
+ }
+
+ public void testBasicFunctions() {
+ ArrayRealVector v1 = new ArrayRealVector(vec1);
+ ArrayRealVector v2 = new ArrayRealVector(vec2);
+ ArrayRealVector v5 = new ArrayRealVector(vec5);
+ ArrayRealVector v_null = new ArrayRealVector(vec_null);
+
+ RealVectorTestImpl v2_t = new RealVectorTestImpl(vec2);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] A --> 8.4261497731763586307
+ double d_getNorm = v5.getNorm();
+ assertEquals("compare values ", 8.4261497731763586307, d_getNorm);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] vN --> 17
+ double d_getL1Norm = v5.getL1Norm();
+ assertEquals("compare values ", 17.0, d_getL1Norm);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] vn --> 6
+ double d_getLInfNorm = v5.getLInfNorm();
+ assertEquals("compare values ", 6.0, d_getLInfNorm);
+
+
+ //octave = sqrt(sumsq(v1-v2))
+ double dist = v1.getDistance(v2);
+ assertEquals("compare values ",v1.subtract(v2).getNorm(), dist );
+
+ //octave = sqrt(sumsq(v1-v2))
+ double dist_2 = v1.getDistance(v2_t);
+ assertEquals("compare values ", v1.subtract(v2).getNorm(),dist_2 );
+
+ //octave = sqrt(sumsq(v1-v2))
+ double dist_3 = v1.getDistance((RealVector) v2);
+ assertEquals("compare values ", v1.subtract(v2).getNorm(),dist_3 );
+
+ //octave = ???
+ double d_getL1Distance = v1. getL1Distance(v2);
+ assertEquals("compare values ",9d, d_getL1Distance );
+
+ double d_getL1Distance_2 = v1. getL1Distance(v2_t);
+ assertEquals("compare values ",9d, d_getL1Distance_2 );
+
+ double d_getL1Distance_3 = v1. getL1Distance((RealVector) v2);
+ assertEquals("compare values ",9d, d_getL1Distance_3 );
+
+ //octave = ???
+ double d_getLInfDistance = v1. getLInfDistance(v2);
+ assertEquals("compare values ",3d, d_getLInfDistance );
+
+ double d_getLInfDistance_2 = v1. getLInfDistance(v2_t);
+ assertEquals("compare values ",3d, d_getLInfDistance_2 );
+
+ double d_getLInfDistance_3 = v1. getLInfDistance((RealVector) v2);
+ assertEquals("compare values ",3d, d_getLInfDistance_3 );
+
+ //octave = v1 + v2
+ ArrayRealVector v_add = v1.add(v2);
+ double[] result_add = {5d, 7d, 9d};
+ assertClose("compare vect" ,v_add.getData(),result_add,normTolerance);
+
+ RealVectorTestImpl vt2 = new RealVectorTestImpl(vec2);
+ RealVector v_add_i = v1.add(vt2);
+ double[] result_add_i = {5d, 7d, 9d};
+ assertClose("compare vect" ,v_add_i.getData(),result_add_i,normTolerance);
+
+ //octave = v1 - v2
+ ArrayRealVector v_subtract = v1.subtract(v2);
+ double[] result_subtract = {-3d, -3d, -3d};
+ assertClose("compare vect" ,v_subtract.getData(),result_subtract,normTolerance);
+
+ RealVector v_subtract_i = v1.subtract(vt2);
+ double[] result_subtract_i = {-3d, -3d, -3d};
+ assertClose("compare vect" ,v_subtract_i.getData(),result_subtract_i,normTolerance);
+
+ // octave v1 .* v2
+ ArrayRealVector v_ebeMultiply = v1.ebeMultiply(v2);
+ double[] result_ebeMultiply = {4d, 10d, 18d};
+ assertClose("compare vect" ,v_ebeMultiply.getData(),result_ebeMultiply,normTolerance);
+
+ RealVector v_ebeMultiply_2 = v1.ebeMultiply(v2_t);
+ double[] result_ebeMultiply_2 = {4d, 10d, 18d};
+ assertClose("compare vect" ,v_ebeMultiply_2.getData(),result_ebeMultiply_2,normTolerance);
+
+ RealVector v_ebeMultiply_3 = v1.ebeMultiply((RealVector) v2);
+ double[] result_ebeMultiply_3 = {4d, 10d, 18d};
+ assertClose("compare vect" ,v_ebeMultiply_3.getData(),result_ebeMultiply_3,normTolerance);
+
+ // octave v1 ./ v2
+ ArrayRealVector v_ebeDivide = v1.ebeDivide(v2);
+ double[] result_ebeDivide = {0.25d, 0.4d, 0.5d};
+ assertClose("compare vect" ,v_ebeDivide.getData(),result_ebeDivide,normTolerance);
+
+ RealVector v_ebeDivide_2 = v1.ebeDivide(v2_t);
+ double[] result_ebeDivide_2 = {0.25d, 0.4d, 0.5d};
+ assertClose("compare vect" ,v_ebeDivide_2.getData(),result_ebeDivide_2,normTolerance);
+
+ RealVector v_ebeDivide_3 = v1.ebeDivide((RealVector) v2);
+ double[] result_ebeDivide_3 = {0.25d, 0.4d, 0.5d};
+ assertClose("compare vect" ,v_ebeDivide_3.getData(),result_ebeDivide_3,normTolerance);
+
+ // octave dot(v1,v2)
+ double dot = v1.dotProduct(v2);
+ assertEquals("compare val ",32d, dot);
+
+ // octave dot(v1,v2_t)
+ double dot_2 = v1.dotProduct(v2_t);
+ assertEquals("compare val ",32d, dot_2);
+
+ RealMatrix m_outerProduct = v1.outerProduct(v2);
+ assertEquals("compare val ",4d, m_outerProduct.getEntry(0,0));
+
+ RealMatrix m_outerProduct_2 = v1.outerProduct(v2_t);
+ assertEquals("compare val ",4d, m_outerProduct_2.getEntry(0,0));
+
+ RealMatrix m_outerProduct_3 = v1.outerProduct((RealVector) v2);
+ assertEquals("compare val ",4d, m_outerProduct_3.getEntry(0,0));
+
+ RealVector v_unitVector = v1.unitVector();
+ RealVector v_unitVector_2 = v1.mapDivide(v1.getNorm());
+ assertClose("compare vect" ,v_unitVector.getData(),v_unitVector_2.getData(),normTolerance);
+
+ try {
+ v_null.unitVector();
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_unitize = (ArrayRealVector)v1.copy();
+ v_unitize.unitize();
+ assertClose("compare vect" ,v_unitVector_2.getData(),v_unitize.getData(),normTolerance);
+ try {
+ v_null.unitize();
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected behavior
+ }
+
+ ArrayRealVector v_projection = v1.projection(v2);
+ double[] result_projection = {1.662337662337662, 2.0779220779220777, 2.493506493506493};
+ assertClose("compare vect", v_projection.getData(), result_projection, normTolerance);
+
+ RealVector v_projection_2 = v1.projection(v2_t);
+ double[] result_projection_2 = {1.662337662337662, 2.0779220779220777, 2.493506493506493};
+ assertClose("compare vect", v_projection_2.getData(), result_projection_2, normTolerance);
+
+ RealVector v_projection_3 = v1.projection(v2.getData());
+ double[] result_projection_3 = {1.662337662337662, 2.0779220779220777, 2.493506493506493};
+ assertClose("compare vect", v_projection_3.getData(), result_projection_3, normTolerance);
+
+ }
+
+ public void testMisc() {
+ ArrayRealVector v1 = new ArrayRealVector(vec1);
+ ArrayRealVector v4 = new ArrayRealVector(vec4);
+ RealVector v4_2 = new ArrayRealVector(vec4);
+
+ String out1 = v1.toString();
+ assertTrue("some output ", out1.length()!=0);
+ /*
+ double[] dout1 = v1.copyOut();
+ assertEquals("testData len", 3, dout1.length);
+ assertNotSame("testData not same object ", v1.data, dout1);
+ */
+ try {
+ v1.checkVectorDimensions(2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ try {
+ v1.checkVectorDimensions(v4);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ try {
+ v1.checkVectorDimensions(v4_2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+ }
+
+ public void testPredicates() {
+
+ ArrayRealVector v = new ArrayRealVector(new double[] { 0, 1, 2 });
+
+ assertFalse(v.isNaN());
+ v.setEntry(1, Double.NaN);
+ assertTrue(v.isNaN());
+
+ assertFalse(v.isInfinite());
+ v.setEntry(0, Double.POSITIVE_INFINITY);
+ assertFalse(v.isInfinite());
+ v.setEntry(1, 1);
+ assertTrue(v.isInfinite());
+ v.setEntry(0, 1);
+ assertFalse(v.isInfinite());
+
+ v.setEntry(0, 0);
+ assertEquals(v, new ArrayRealVector(new double[] { 0, 1, 2 }));
+ assertNotSame(v, new ArrayRealVector(new double[] { 0, 1, 2 + FastMath.ulp(2)}));
+ assertNotSame(v, new ArrayRealVector(new double[] { 0, 1, 2, 3 }));
+
+ assertEquals(new ArrayRealVector(new double[] { Double.NaN, 1, 2 }).hashCode(),
+ new ArrayRealVector(new double[] { 0, Double.NaN, 2 }).hashCode());
+
+ assertTrue(new ArrayRealVector(new double[] { Double.NaN, 1, 2 }).hashCode() !=
+ new ArrayRealVector(new double[] { 0, 1, 2 }).hashCode());
+
+ assertTrue(v.equals(v));
+ assertTrue(v.equals(v.copy()));
+ assertFalse(v.equals(null));
+ assertFalse(v.equals(v.getDataRef()));
+ assertFalse(v.equals(v.getSubVector(0, v.getDimension() - 1)));
+ assertTrue(v.equals(v.getSubVector(0, v.getDimension())));
+
+ }
+
+ public void testSerial() {
+ ArrayRealVector v = new ArrayRealVector(new double[] { 0, 1, 2 });
+ assertEquals(v,TestUtils.serializeAndRecover(v));
+ }
+
+ public void testZeroVectors() {
+ assertEquals(0, new ArrayRealVector(new double[0]).getDimension());
+ assertEquals(0, new ArrayRealVector(new double[0], true).getDimension());
+ assertEquals(0, new ArrayRealVector(new double[0], false).getDimension());
+ }
+
+ public void testMinMax() {
+ ArrayRealVector v1 = new ArrayRealVector(new double[] { 0, -6, 4, 12, 7 });
+ assertEquals(1, v1.getMinIndex());
+ assertEquals(-6, v1.getMinValue(), 1.0e-12);
+ assertEquals(3, v1.getMaxIndex());
+ assertEquals(12, v1.getMaxValue(), 1.0e-12);
+ ArrayRealVector v2 = new ArrayRealVector(new double[] { Double.NaN, 3, Double.NaN, -2 });
+ assertEquals(3, v2.getMinIndex());
+ assertEquals(-2, v2.getMinValue(), 1.0e-12);
+ assertEquals(1, v2.getMaxIndex());
+ assertEquals(3, v2.getMaxValue(), 1.0e-12);
+ ArrayRealVector v3 = new ArrayRealVector(new double[] { Double.NaN, Double.NaN });
+ assertEquals(-1, v3.getMinIndex());
+ assertTrue(Double.isNaN(v3.getMinValue()));
+ assertEquals(-1, v3.getMaxIndex());
+ assertTrue(Double.isNaN(v3.getMaxValue()));
+ ArrayRealVector v4 = new ArrayRealVector(new double[0]);
+ assertEquals(-1, v4.getMinIndex());
+ assertTrue(Double.isNaN(v4.getMinValue()));
+ assertEquals(-1, v4.getMaxIndex());
+ assertTrue(Double.isNaN(v4.getMaxValue()));
+ }
+
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, double[] m, double[] n,
+ double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors have different lengths");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i],n[i],tolerance);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/BiDiagonalTransformerTest.java b/src/test/java/org/apache/commons/math/linear/BiDiagonalTransformerTest.java
new file mode 100644
index 0000000..ce732ae
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/BiDiagonalTransformerTest.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import org.apache.commons.math.linear.BiDiagonalTransformer;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BiDiagonalTransformerTest {
+
+ private double[][] testSquare = {
+ { 24.0 / 25.0, 43.0 / 25.0 },
+ { 57.0 / 25.0, 24.0 / 25.0 }
+ };
+
+ private double[][] testNonSquare = {
+ { -540.0 / 625.0, 963.0 / 625.0, -216.0 / 625.0 },
+ { -1730.0 / 625.0, -744.0 / 625.0, 1008.0 / 625.0 },
+ { -720.0 / 625.0, 1284.0 / 625.0, -288.0 / 625.0 },
+ { -360.0 / 625.0, 192.0 / 625.0, 1756.0 / 625.0 },
+ };
+
+ @Test
+ public void testDimensions() {
+ checkdimensions(MatrixUtils.createRealMatrix(testSquare));
+ checkdimensions(MatrixUtils.createRealMatrix(testNonSquare));
+ checkdimensions(MatrixUtils.createRealMatrix(testNonSquare).transpose());
+ }
+
+ private void checkdimensions(RealMatrix matrix) {
+ final int m = matrix.getRowDimension();
+ final int n = matrix.getColumnDimension();
+ BiDiagonalTransformer transformer = new BiDiagonalTransformer(matrix);
+ Assert.assertEquals(m, transformer.getU().getRowDimension());
+ Assert.assertEquals(m, transformer.getU().getColumnDimension());
+ Assert.assertEquals(m, transformer.getB().getRowDimension());
+ Assert.assertEquals(n, transformer.getB().getColumnDimension());
+ Assert.assertEquals(n, transformer.getV().getRowDimension());
+ Assert.assertEquals(n, transformer.getV().getColumnDimension());
+
+ }
+
+ @Test
+ public void testAEqualUSVt() {
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testSquare));
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare));
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare).transpose());
+ }
+
+ private void checkAEqualUSVt(RealMatrix matrix) {
+ BiDiagonalTransformer transformer = new BiDiagonalTransformer(matrix);
+ RealMatrix u = transformer.getU();
+ RealMatrix b = transformer.getB();
+ RealMatrix v = transformer.getV();
+ double norm = u.multiply(b).multiply(v.transpose()).subtract(matrix).getNorm();
+ Assert.assertEquals(0, norm, 1.0e-14);
+ }
+
+ @Test
+ public void testUOrthogonal() {
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare)).getU());
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare)).getU());
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getU());
+ }
+
+ @Test
+ public void testVOrthogonal() {
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare)).getV());
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare)).getV());
+ checkOrthogonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getV());
+ }
+
+ private void checkOrthogonal(RealMatrix m) {
+ RealMatrix mTm = m.transpose().multiply(m);
+ RealMatrix id = MatrixUtils.createRealIdentityMatrix(mTm.getRowDimension());
+ Assert.assertEquals(0, mTm.subtract(id).getNorm(), 1.0e-14);
+ }
+
+ @Test
+ public void testBBiDiagonal() {
+ checkBiDiagonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare)).getB());
+ checkBiDiagonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare)).getB());
+ checkBiDiagonal(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getB());
+ }
+
+ private void checkBiDiagonal(RealMatrix m) {
+ final int rows = m.getRowDimension();
+ final int cols = m.getColumnDimension();
+ for (int i = 0; i < rows; ++i) {
+ for (int j = 0; j < cols; ++j) {
+ if (rows < cols) {
+ if ((i < j) || (i > j + 1)) {
+ Assert.assertEquals(0, m.getEntry(i, j), 1.0e-16);
+ }
+ } else {
+ if ((i < j - 1) || (i > j)) {
+ Assert.assertEquals(0, m.getEntry(i, j), 1.0e-16);
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testSingularMatrix() {
+ BiDiagonalTransformer transformer =
+ new BiDiagonalTransformer(MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 2.0, 3.0 },
+ { 2.0, 3.0, 4.0 },
+ { 3.0, 5.0, 7.0 }
+ }));
+ final double s3 = FastMath.sqrt(3.0);
+ final double s14 = FastMath.sqrt(14.0);
+ final double s1553 = FastMath.sqrt(1553.0);
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -1.0 / s14, 5.0 / (s3 * s14), 1.0 / s3 },
+ { -2.0 / s14, -4.0 / (s3 * s14), 1.0 / s3 },
+ { -3.0 / s14, 1.0 / (s3 * s14), -1.0 / s3 }
+ });
+ RealMatrix bRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -s14, s1553 / s14, 0.0 },
+ { 0.0, -87 * s3 / (s14 * s1553), -s3 * s14 / s1553 },
+ { 0.0, 0.0, 0.0 }
+ });
+ RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, -23 / s1553, 32 / s1553 },
+ { 0.0, -32 / s1553, -23 / s1553 }
+ });
+
+ // check values against known references
+ RealMatrix u = transformer.getU();
+ Assert.assertEquals(0, u.subtract(uRef).getNorm(), 1.0e-14);
+ RealMatrix b = transformer.getB();
+ Assert.assertEquals(0, b.subtract(bRef).getNorm(), 1.0e-14);
+ RealMatrix v = transformer.getV();
+ Assert.assertEquals(0, v.subtract(vRef).getNorm(), 1.0e-14);
+
+ // check the same cached instance is returned the second time
+ Assert.assertTrue(u == transformer.getU());
+ Assert.assertTrue(b == transformer.getB());
+ Assert.assertTrue(v == transformer.getV());
+
+ }
+
+ @Test
+ public void testMatricesValues() {
+ BiDiagonalTransformer transformer =
+ new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare));
+ final double s17 = FastMath.sqrt(17.0);
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -8 / (5 * s17), 19 / (5 * s17) },
+ { -19 / (5 * s17), -8 / (5 * s17) }
+ });
+ RealMatrix bRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -3 * s17 / 5, 32 * s17 / 85 },
+ { 0.0, -5 * s17 / 17 }
+ });
+ RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 0.0 },
+ { 0.0, -1.0 }
+ });
+
+ // check values against known references
+ RealMatrix u = transformer.getU();
+ Assert.assertEquals(0, u.subtract(uRef).getNorm(), 1.0e-14);
+ RealMatrix b = transformer.getB();
+ Assert.assertEquals(0, b.subtract(bRef).getNorm(), 1.0e-14);
+ RealMatrix v = transformer.getV();
+ Assert.assertEquals(0, v.subtract(vRef).getNorm(), 1.0e-14);
+
+ // check the same cached instance is returned the second time
+ Assert.assertTrue(u == transformer.getU());
+ Assert.assertTrue(b == transformer.getB());
+ Assert.assertTrue(v == transformer.getV());
+
+ }
+
+ @Test
+ public void testUpperOrLower() {
+ Assert.assertTrue(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare)).isUpperBiDiagonal());
+ Assert.assertTrue(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare)).isUpperBiDiagonal());
+ Assert.assertFalse(new BiDiagonalTransformer(MatrixUtils.createRealMatrix(testNonSquare).transpose()).isUpperBiDiagonal());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/BigMatrixImplTest.java b/src/test/java/org/apache/commons/math/linear/BigMatrixImplTest.java
new file mode 100644
index 0000000..4ed2e6b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/BigMatrixImplTest.java
@@ -0,0 +1,836 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for the {@link BigMatrixImpl} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+ at Deprecated
+public final class BigMatrixImplTest extends TestCase {
+
+ // Test data for String constructors
+ protected String[][] testDataString = { {"1","2","3"}, {"2","5","3"}, {"1","0","8"} };
+
+ // 3 x 3 identity matrix
+ protected double[][] id = { {1d,0d,0d}, {0d,1d,0d}, {0d,0d,1d} };
+
+ // Test data for group operations
+ protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
+ protected double[][] testDataLU = {{2d, 5d, 3d}, {.5d, -2.5d, 6.5d}, {0.5d, 0.2d, .2d}};
+ protected double[][] testDataPlus2 = { {3d,4d,5d}, {4d,7d,5d}, {3d,2d,10d} };
+ protected double[][] testDataMinus = { {-1d,-2d,-3d}, {-2d,-5d,-3d},
+ {-1d,0d,-8d} };
+ protected double[] testDataRow1 = {1d,2d,3d};
+ protected double[] testDataCol3 = {3d,3d,8d};
+ protected double[][] testDataInv =
+ { {-40d,16d,9d}, {13d,-5d,-3d}, {5d,-2d,-1d} };
+ protected double[] preMultTest = {8,12,33};
+ protected double[][] testData2 ={ {1d,2d,3d}, {2d,5d,3d}};
+ protected double[][] testData2T = { {1d,2d}, {2d,5d}, {3d,3d}};
+ protected double[][] testDataPlusInv =
+ { {-39d,18d,12d}, {15d,0d,0d}, {6d,-2d,7d} };
+
+ // lu decomposition tests
+ protected double[][] luData = { {2d,3d,3d}, {0d,5d,7d}, {6d,9d,8d} };
+ protected double[][] luDataLUDecomposition = { {6d,9d,8d}, {0d,5d,7d},
+ {0.33333333333333,0d,0.33333333333333} };
+
+ // singular matrices
+ protected double[][] singular = { {2d,3d}, {2d,3d} };
+ protected double[][] bigSingular = {{1d,2d,3d,4d}, {2d,5d,3d,4d},
+ {7d,3d,256d,1930d}, {3d,7d,6d,8d}}; // 4th row = 1st + 2nd
+ protected double[][] detData = { {1d,2d,3d}, {4d,5d,6d}, {7d,8d,10d} };
+ protected double[][] detData2 = { {1d, 3d}, {2d, 4d}};
+
+ // vectors
+ protected double[] testVector = {1,2,3};
+ protected double[] testVector2 = {1,2,3,4};
+
+ // submatrix accessor tests
+ protected double[][] subTestData = {{1, 2, 3, 4}, {1.5, 2.5, 3.5, 4.5},
+ {2, 4, 6, 8}, {4, 5, 6, 7}};
+ // array selections
+ protected double[][] subRows02Cols13 = { {2, 4}, {4, 8}};
+ protected double[][] subRows03Cols12 = { {2, 3}, {5, 6}};
+ protected double[][] subRows03Cols123 = { {2, 3, 4} , {5, 6, 7}};
+ // effective permutations
+ protected double[][] subRows20Cols123 = { {4, 6, 8} , {2, 3, 4}};
+ protected double[][] subRows31Cols31 = {{7, 5}, {4.5, 2.5}};
+ // contiguous ranges
+ protected double[][] subRows01Cols23 = {{3,4} , {3.5, 4.5}};
+ protected double[][] subRows23Cols00 = {{2} , {4}};
+ protected double[][] subRows00Cols33 = {{4}};
+ // row matrices
+ protected double[][] subRow0 = {{1,2,3,4}};
+ protected double[][] subRow3 = {{4,5,6,7}};
+ // column matrices
+ protected double[][] subColumn1 = {{2}, {2.5}, {4}, {5}};
+ protected double[][] subColumn3 = {{4}, {4.5}, {8}, {7}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public BigMatrixImplTest(String name) {
+ super(name);
+ }
+
+ public static final double[] asDouble(BigDecimal[] data) {
+ double d[] = new double[data.length];
+ for (int i=0;i<d.length;i++) {
+ d[i] = data[i].doubleValue();
+ }
+ return d;
+ }
+
+ public static final double[][] asDouble(BigDecimal[][] data) {
+ double d[][] = new double[data.length][data[0].length];
+ for (int i=0;i<d.length;i++) {
+ for (int j=0;j<d[i].length;j++)
+ d[i][j] = data[i][j].doubleValue();
+ }
+ return d;
+ }
+
+ public static final BigDecimal[] asBigDecimal(double [] data) {
+ BigDecimal d[] = new BigDecimal[data.length];
+ for (int i=0;i<d.length;i++) {
+ d[i] = new BigDecimal(data[i]);
+ }
+ return d;
+ }
+
+ public static final BigDecimal[][] asBigDecimal(double [][] data) {
+ BigDecimal d[][] = new BigDecimal[data.length][data[0].length];
+ for (int i=0;i<d.length;i++) {
+ for (int j=0;j<data[i].length;j++) {
+ d[i][j] = new BigDecimal(data[i][j]);
+ }
+ }
+ return d;
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl m2 = new BigMatrixImpl(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ BigMatrixImpl m1 = new BigMatrixImpl(testData);
+ BigMatrixImpl m2 = new BigMatrixImpl(m1.getData());
+ assertEquals(m2,m1);
+ BigMatrixImpl m3 = new BigMatrixImpl(testData);
+ BigMatrixImpl m4 = new BigMatrixImpl(m3.getData(), false);
+ assertEquals(m4,m3);
+ }
+
+ /** test constructors */
+ public void testConstructors() {
+ BigMatrix m1 = new BigMatrixImpl(testData);
+ BigMatrix m2 = new BigMatrixImpl(testDataString);
+ BigMatrix m3 = new BigMatrixImpl(asBigDecimal(testData));
+ BigMatrix m4 = new BigMatrixImpl(asBigDecimal(testData), true);
+ BigMatrix m5 = new BigMatrixImpl(asBigDecimal(testData), false);
+ assertClose("double, string", m1, m2, Double.MIN_VALUE);
+ assertClose("double, BigDecimal", m1, m3, Double.MIN_VALUE);
+ assertClose("string, BigDecimal", m2, m3, Double.MIN_VALUE);
+ assertClose("double, BigDecimal/true", m1, m4, Double.MIN_VALUE);
+ assertClose("double, BigDecimal/false", m1, m5, Double.MIN_VALUE);
+ try {
+ new BigMatrixImpl(new String[][] {{"0", "hello", "1"}});
+ fail("Expecting NumberFormatException");
+ } catch (NumberFormatException ex) {
+ // expected
+ }
+ try {
+ new BigMatrixImpl(new String[][] {});
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ new BigMatrixImpl(new String[][] {{},{}});
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ new BigMatrixImpl(new String[][] {{"a", "b"},{"c"}});
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ new BigMatrixImpl(0, 1);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ new BigMatrixImpl(1, 0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ /** test add */
+ public void testAdd() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl mInv = new BigMatrixImpl(testDataInv);
+ BigMatrix mPlusMInv = m.add(mInv);
+ double[][] sumEntries = asDouble(mPlusMInv.getData());
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ testDataPlusInv[row][col],sumEntries[row][col],
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl m2 = new BigMatrixImpl(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test norm */
+ public void testNorm() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl m2 = new BigMatrixImpl(testData2);
+ assertEquals("testData norm",14d,m.getNorm().doubleValue(),entryTolerance);
+ assertEquals("testData2 norm",7d,m2.getNorm().doubleValue(),entryTolerance);
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl m2 = new BigMatrixImpl(testDataInv);
+ assertClose("m-n = m + -n",m.subtract(m2),
+ m2.scalarMultiply(new BigDecimal(-1d)).add(m),entryTolerance);
+ try {
+ m.subtract(new BigMatrixImpl(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl mInv = new BigMatrixImpl(testDataInv);
+ BigMatrixImpl identity = new BigMatrixImpl(id);
+ BigMatrixImpl m2 = new BigMatrixImpl(testData2);
+ assertClose("inverse multiply",m.multiply(mInv),
+ identity,entryTolerance);
+ assertClose("inverse multiply",mInv.multiply(m),
+ identity,entryTolerance);
+ assertClose("identity multiply",m.multiply(identity),
+ m,entryTolerance);
+ assertClose("identity multiply",identity.multiply(mInv),
+ mInv,entryTolerance);
+ assertClose("identity multiply",m2.multiply(identity),
+ m2,entryTolerance);
+ try {
+ m.multiply(new BigMatrixImpl(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ //Additional Test for BigMatrixImplTest.testMultiply
+
+ private double[][] d3 = new double[][] {{1,2,3,4},{5,6,7,8}};
+ private double[][] d4 = new double[][] {{1},{2},{3},{4}};
+ private double[][] d5 = new double[][] {{30},{70}};
+
+ public void testMultiply2() {
+ BigMatrix m3 = new BigMatrixImpl(d3);
+ BigMatrix m4 = new BigMatrixImpl(d4);
+ BigMatrix m5 = new BigMatrixImpl(d5);
+ assertClose("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test isSingular */
+ public void testIsSingular() {
+ BigMatrixImpl m = new BigMatrixImpl(singular);
+ assertTrue("singular",m.isSingular());
+ m = new BigMatrixImpl(bigSingular);
+ assertTrue("big singular",m.isSingular());
+ m = new BigMatrixImpl(id);
+ assertTrue("identity nonsingular",!m.isSingular());
+ m = new BigMatrixImpl(testData);
+ assertTrue("testData nonsingular",!m.isSingular());
+ }
+
+ /** test inverse */
+ public void testInverse() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrix mInv = new BigMatrixImpl(testDataInv);
+ assertClose("inverse",mInv,m.inverse(),normTolerance);
+ assertClose("inverse^2",m,m.inverse().inverse(),10E-12);
+
+ // Not square
+ m = new BigMatrixImpl(testData2);
+ try {
+ m.inverse();
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+
+ // Singular
+ m = new BigMatrixImpl(singular);
+ try {
+ m.inverse();
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ /** test solve */
+ public void testSolve() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrix mInv = new BigMatrixImpl(testDataInv);
+ // being a bit slothful here -- actually testing that X = A^-1 * B
+ assertClose("inverse-operate",
+ asDouble(mInv.operate(asBigDecimal(testVector))),
+ asDouble(m.solve(asBigDecimal(testVector))),
+ normTolerance);
+ try {
+ asDouble(m.solve(asBigDecimal(testVector2)));
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ BigMatrix bs = new BigMatrixImpl(bigSingular);
+ try {
+ bs.solve(bs);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // ignored
+ }
+ try {
+ m.solve(bs);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ new BigMatrixImpl(testData2).solve(bs);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ (new BigMatrixImpl(testData2)).luDecompose();
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test determinant */
+ public void testDeterminant() {
+ BigMatrix m = new BigMatrixImpl(bigSingular);
+ assertEquals("singular determinant",0,m.getDeterminant().doubleValue(),0);
+ m = new BigMatrixImpl(detData);
+ assertEquals("nonsingular test",-3d,m.getDeterminant().doubleValue(),normTolerance);
+
+ // Examples verified against R (version 1.8.1, Red Hat Linux 9)
+ m = new BigMatrixImpl(detData2);
+ assertEquals("nonsingular R test 1",-2d,m.getDeterminant().doubleValue(),normTolerance);
+ m = new BigMatrixImpl(testData);
+ assertEquals("nonsingular R test 2",-1d,m.getDeterminant().doubleValue(),normTolerance);
+
+ try {
+ double d = new BigMatrixImpl(testData2).getDeterminant().doubleValue();
+ fail("Expecting InvalidMatrixException, got " + d);
+ } catch (InvalidMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test trace */
+ public void testTrace() {
+ BigMatrix m = new BigMatrixImpl(id);
+ assertEquals("identity trace",3d,m.getTrace().doubleValue(),entryTolerance);
+ m = new BigMatrixImpl(testData2);
+ try {
+ double t = m.getTrace().doubleValue();
+ fail("Expecting NonSquareMatrixException, got " + t);
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ BigMatrix m = new BigMatrixImpl(testData);
+ assertClose("scalar add",new BigMatrixImpl(testDataPlus2),
+ m.scalarAdd(new BigDecimal(2d)),entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ BigMatrix m = new BigMatrixImpl(id);
+ double[] x = asDouble(m.operate(asBigDecimal(testVector)));
+ assertClose("identity operate",testVector,x,entryTolerance);
+ m = new BigMatrixImpl(bigSingular);
+ try {
+ asDouble(m.operate(asBigDecimal(testVector)));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ BigMatrix a = new BigMatrixImpl(new BigDecimal[][] {
+ { new BigDecimal(1), new BigDecimal(2) },
+ { new BigDecimal(3), new BigDecimal(4) },
+ { new BigDecimal(5), new BigDecimal(6) }
+ }, false);
+ BigDecimal[] b = a.operate(new BigDecimal[] { new BigDecimal(1), new BigDecimal(1) });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( 3.0, b[0].doubleValue(), 1.0e-12);
+ assertEquals( 7.0, b[1].doubleValue(), 1.0e-12);
+ assertEquals(11.0, b[2].doubleValue(), 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ BigMatrix m = new BigMatrixImpl(testData);
+ assertClose("inverse-transpose",m.inverse().transpose(),
+ m.transpose().inverse(),normTolerance);
+ m = new BigMatrixImpl(testData2);
+ BigMatrix mt = new BigMatrixImpl(testData2T);
+ assertClose("transpose",mt,m.transpose(),normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ BigMatrix m = new BigMatrixImpl(testData);
+ assertClose("premultiply",asDouble(m.preMultiply(asBigDecimal(testVector))),preMultTest,normTolerance);
+ m = new BigMatrixImpl(bigSingular);
+ try {
+ m.preMultiply(asBigDecimal(testVector));
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ BigMatrix m3 = new BigMatrixImpl(d3);
+ BigMatrix m4 = new BigMatrixImpl(d4);
+ BigMatrix m5 = new BigMatrixImpl(d5);
+ assertClose("m3*m4=m5", m4.preMultiply(m3), m5, entryTolerance);
+
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl mInv = new BigMatrixImpl(testDataInv);
+ BigMatrixImpl identity = new BigMatrixImpl(id);
+ new BigMatrixImpl(testData2);
+ assertClose("inverse multiply",m.preMultiply(mInv),
+ identity,entryTolerance);
+ assertClose("inverse multiply",mInv.preMultiply(m),
+ identity,entryTolerance);
+ assertClose("identity multiply",m.preMultiply(identity),
+ m,entryTolerance);
+ assertClose("identity multiply",identity.preMultiply(mInv),
+ mInv,entryTolerance);
+ try {
+ m.preMultiply(new BigMatrixImpl(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ BigMatrix m = new BigMatrixImpl(testData);
+ assertClose("get row",m.getRowAsDoubleArray(0),testDataRow1,entryTolerance);
+ assertClose("get col",m.getColumnAsDoubleArray(2),testDataCol3,entryTolerance);
+ try {
+ m.getRowAsDoubleArray(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumnAsDoubleArray(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testLUDecomposition() throws Exception {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrix lu = m.getLUMatrix();
+ assertClose("LU decomposition", lu, new BigMatrixImpl(testDataLU), normTolerance);
+ verifyDecomposition(m, lu);
+ m = new BigMatrixImpl(luData);
+ lu = m.getLUMatrix();
+ assertClose("LU decomposition", lu, new BigMatrixImpl(luDataLUDecomposition), normTolerance);
+ verifyDecomposition(m, lu);
+ m = new BigMatrixImpl(testDataMinus);
+ lu = m.getLUMatrix();
+ verifyDecomposition(m, lu);
+ m = new BigMatrixImpl(id);
+ lu = m.getLUMatrix();
+ verifyDecomposition(m, lu);
+ try {
+ m = new BigMatrixImpl(bigSingular); // singular
+ lu = m.getLUMatrix();
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ try {
+ m = new BigMatrixImpl(testData2); // not square
+ lu = m.getLUMatrix();
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * test submatrix accessors
+ */
+ public void testSubMatrix() {
+ BigMatrix m = new BigMatrixImpl(subTestData);
+ BigMatrix mRows23Cols00 = new BigMatrixImpl(subRows23Cols00);
+ BigMatrix mRows00Cols33 = new BigMatrixImpl(subRows00Cols33);
+ BigMatrix mRows01Cols23 = new BigMatrixImpl(subRows01Cols23);
+ BigMatrix mRows02Cols13 = new BigMatrixImpl(subRows02Cols13);
+ BigMatrix mRows03Cols12 = new BigMatrixImpl(subRows03Cols12);
+ BigMatrix mRows03Cols123 = new BigMatrixImpl(subRows03Cols123);
+ BigMatrix mRows20Cols123 = new BigMatrixImpl(subRows20Cols123);
+ BigMatrix mRows31Cols31 = new BigMatrixImpl(subRows31Cols31);
+ assertEquals("Rows23Cols00", mRows23Cols00,
+ m.getSubMatrix(2 , 3 , 0, 0));
+ assertEquals("Rows00Cols33", mRows00Cols33,
+ m.getSubMatrix(0 , 0 , 3, 3));
+ assertEquals("Rows01Cols23", mRows01Cols23,
+ m.getSubMatrix(0 , 1 , 2, 3));
+ assertEquals("Rows02Cols13", mRows02Cols13,
+ m.getSubMatrix(new int[] {0,2}, new int[] {1,3}));
+ assertEquals("Rows03Cols12", mRows03Cols12,
+ m.getSubMatrix(new int[] {0,3}, new int[] {1,2}));
+ assertEquals("Rows03Cols123", mRows03Cols123,
+ m.getSubMatrix(new int[] {0,3}, new int[] {1,2,3}));
+ assertEquals("Rows20Cols123", mRows20Cols123,
+ m.getSubMatrix(new int[] {2,0}, new int[] {1,2,3}));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] {3,1}, new int[] {3,1}));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] {3,1}, new int[] {3,1}));
+
+ try {
+ m.getSubMatrix(1,0,2,4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(-1,1,2,2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1,0,2,2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1,0,2,4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] {}, new int[] {0});
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] {0}, new int[] {4});
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ BigMatrix m = new BigMatrixImpl(subTestData);
+ BigMatrix mColumn1 = new BigMatrixImpl(subColumn1);
+ BigMatrix mColumn3 = new BigMatrixImpl(subColumn3);
+ assertEquals("Column1", mColumn1,
+ m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3,
+ m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowMatrix() {
+ BigMatrix m = new BigMatrixImpl(subTestData);
+ BigMatrix mRow0 = new BigMatrixImpl(subRow0);
+ BigMatrix mRow3 = new BigMatrixImpl(subRow3);
+ assertEquals("Row0", mRow0,
+ m.getRowMatrix(0));
+ assertEquals("Row3", mRow3,
+ m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ BigMatrixImpl m1 = (BigMatrixImpl) m.copy();
+ BigMatrixImpl mt = (BigMatrixImpl) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new BigMatrixImpl(bigSingular)));
+ // Different scales make BigDecimals, so matrices unequal
+ m = new BigMatrixImpl(new String[][] {{"2.0"}});
+ m1 = new BigMatrixImpl(new String[][] {{"2.00"}});
+ assertTrue(m.hashCode() != m1.hashCode());
+ assertFalse(m.equals(m1));
+ }
+
+ public void testToString() {
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ assertEquals("BigMatrixImpl{{1,2,3},{2,5,3},{1,0,8}}",
+ m.toString());
+ m = new BigMatrixImpl();
+ assertEquals("BigMatrixImpl{}",
+ m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ BigDecimal[][] detData3 =
+ MatrixUtils.createBigMatrix(detData2).getData();
+ BigMatrixImpl m = new BigMatrixImpl(testData);
+ m.setSubMatrix(detData3,1,1);
+ BigMatrix expected = MatrixUtils.createBigMatrix
+ (new double[][] {{1.0,2.0,3.0},{2.0,1.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData3,0,0);
+ expected = MatrixUtils.createBigMatrix
+ (new double[][] {{1.0,3.0,3.0},{2.0,4.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ BigDecimal[][] testDataPlus3 =
+ MatrixUtils.createBigMatrix(testDataPlus2).getData();
+ m.setSubMatrix(testDataPlus3,0,0);
+ expected = MatrixUtils.createBigMatrix
+ (new double[][] {{3.0,4.0,5.0},{4.0,7.0,5.0},{3.0,2.0,10.0}});
+ assertEquals(expected, m);
+
+ // javadoc example
+ BigMatrixImpl matrix = (BigMatrixImpl) MatrixUtils.createBigMatrix
+ (new double[][] {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 0, 1 , 2}});
+ matrix.setSubMatrix(new BigDecimal[][] {{new BigDecimal(3),
+ new BigDecimal(4)}, {new BigDecimal(5), new BigDecimal(6)}}, 1, 1);
+ expected = MatrixUtils.createBigMatrix
+ (new BigDecimal[][] {{new BigDecimal(1), new BigDecimal(2),
+ new BigDecimal(3), new BigDecimal(4)}, {new BigDecimal(5),
+ new BigDecimal(3), new BigDecimal(4), new BigDecimal(8)},
+ {new BigDecimal(9), new BigDecimal(5) , new BigDecimal(6),
+ new BigDecimal(2)}});
+ assertEquals(expected, matrix);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(matrix.getData(),1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new BigDecimal[][] {{new BigDecimal(1)},
+ {new BigDecimal(2), new BigDecimal(3)}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new BigDecimal[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ //--------------- -----------------Protected methods
+
+ /** verifies that two matrices are close (1-norm) */
+ protected void assertClose(String msg, BigMatrix m, BigMatrix n,
+ double tolerance) {
+ assertTrue(msg,m.subtract(n).getNorm().doubleValue() < tolerance);
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, double[] m, double[] n,
+ double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors not same length");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ",
+ m[i],n[i],tolerance);
+ }
+ }
+
+ /** extracts the l and u matrices from compact lu representation */
+ protected void splitLU(BigMatrix lu, BigDecimal[][] lowerData, BigDecimal[][] upperData) throws InvalidMatrixException {
+ if (!lu.isSquare() || lowerData.length != lowerData[0].length || upperData.length != upperData[0].length ||
+ lowerData.length != upperData.length
+ || lowerData.length != lu.getRowDimension()) {
+ throw new InvalidMatrixException("incorrect dimensions");
+ }
+ int n = lu.getRowDimension();
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (j < i) {
+ lowerData[i][j] = lu.getEntry(i, j);
+ upperData[i][j] = new BigDecimal(0);
+ } else if (i == j) {
+ lowerData[i][j] = new BigDecimal(1);
+ upperData[i][j] = lu.getEntry(i, j);
+ } else {
+ lowerData[i][j] = new BigDecimal(0);
+ upperData[i][j] = lu.getEntry(i, j);
+ }
+ }
+ }
+ }
+
+ /** Returns the result of applying the given row permutation to the matrix */
+ protected BigMatrix permuteRows(BigMatrix matrix, int[] permutation) {
+ if (!matrix.isSquare() || matrix.getRowDimension() != permutation.length) {
+ throw new IllegalArgumentException("dimension mismatch");
+ }
+ int n = matrix.getRowDimension();
+ int m = matrix.getColumnDimension();
+ BigDecimal out[][] = new BigDecimal[m][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ out[i][j] = matrix.getEntry(permutation[i], j);
+ }
+ }
+ return new BigMatrixImpl(out);
+ }
+
+ /** Extracts l and u matrices from lu and verifies that matrix = l times u modulo permutation */
+ protected void verifyDecomposition(BigMatrix matrix, BigMatrix lu) throws Exception{
+ int n = matrix.getRowDimension();
+ BigDecimal[][] lowerData = new BigDecimal[n][n];
+ BigDecimal[][] upperData = new BigDecimal[n][n];
+ splitLU(lu, lowerData, upperData);
+ BigMatrix lower =new BigMatrixImpl(lowerData);
+ BigMatrix upper = new BigMatrixImpl(upperData);
+ int[] permutation = ((BigMatrixImpl) matrix).getPermutation();
+ BigMatrix permuted = permuteRows(matrix, permutation);
+ assertClose("lu decomposition does not work", permuted,
+ lower.multiply(upper), normTolerance);
+ }
+
+// /** Useful for debugging */
+// private void dumpMatrix(BigMatrix m) {
+// for (int i = 0; i < m.getRowDimension(); i++) {
+// String os = "";
+// for (int j = 0; j < m.getColumnDimension(); j++) {
+// os += m.getEntry(i, j) + " ";
+// }
+// System.out.println(os);
+// }
+// }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/BlockFieldMatrixTest.java b/src/test/java/org/apache/commons/math/linear/BlockFieldMatrixTest.java
new file mode 100644
index 0000000..e7e4fd0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/BlockFieldMatrixTest.java
@@ -0,0 +1,1288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionField;
+
+/**
+ * Test cases for the {@link BlockFieldMatrix} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+
+public final class BlockFieldMatrixTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected Fraction[][] id = {
+ {new Fraction(1),new Fraction(0),new Fraction(0)},
+ {new Fraction(0),new Fraction(1),new Fraction(0)},
+ {new Fraction(0),new Fraction(0),new Fraction(1)}
+ };
+
+ // Test data for group operations
+ protected Fraction[][] testData = {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(2),new Fraction(5),new Fraction(3)},
+ {new Fraction(1),new Fraction(0),new Fraction(8)}
+ };
+ protected Fraction[][] testDataLU = {
+ {new Fraction(2), new Fraction(5), new Fraction(3)},
+ {new Fraction(1, 2), new Fraction(-5, 2), new Fraction(13, 2)},
+ {new Fraction(1, 2), new Fraction(1, 5), new Fraction(1, 5)}
+ };
+ protected Fraction[][] testDataPlus2 = {
+ {new Fraction(3),new Fraction(4),new Fraction(5)},
+ {new Fraction(4),new Fraction(7),new Fraction(5)},
+ {new Fraction(3),new Fraction(2),new Fraction(10)}
+ };
+ protected Fraction[][] testDataMinus = {
+ {new Fraction(-1),new Fraction(-2),new Fraction(-3)},
+ {new Fraction(-2),new Fraction(-5),new Fraction(-3)},
+ {new Fraction(-1),new Fraction(0),new Fraction(-8)}
+ };
+ protected Fraction[] testDataRow1 = {new Fraction(1),new Fraction(2),new Fraction(3)};
+ protected Fraction[] testDataCol3 = {new Fraction(3),new Fraction(3),new Fraction(8)};
+ protected Fraction[][] testDataInv = {
+ {new Fraction(-40),new Fraction(16),new Fraction(9)},
+ {new Fraction(13),new Fraction(-5),new Fraction(-3)},
+ {new Fraction(5),new Fraction(-2),new Fraction(-1)}
+ };
+ protected Fraction[] preMultTest = {new Fraction(8), new Fraction(12), new Fraction(33)};
+ protected Fraction[][] testData2 = {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(2),new Fraction(5),new Fraction(3)}
+ };
+ protected Fraction[][] testData2T = {
+ {new Fraction(1),new Fraction(2)},
+ {new Fraction(2),new Fraction(5)},
+ {new Fraction(3),new Fraction(3)}
+ };
+ protected Fraction[][] testDataPlusInv = {
+ {new Fraction(-39),new Fraction(18),new Fraction(12)},
+ {new Fraction(15),new Fraction(0),new Fraction(0)},
+ {new Fraction(6),new Fraction(-2),new Fraction(7)}
+ };
+
+ // lu decomposition tests
+ protected Fraction[][] luData = {
+ {new Fraction(2),new Fraction(3),new Fraction(3)},
+ {new Fraction(0),new Fraction(5),new Fraction(7)},
+ {new Fraction(6),new Fraction(9),new Fraction(8)}
+ };
+ protected Fraction[][] luDataLUDecomposition = {
+ {new Fraction(6),new Fraction(9),new Fraction(8)},
+ {new Fraction(0),new Fraction(5),new Fraction(7)},
+ {new Fraction(1, 3),new Fraction(0),new Fraction(1, 3)}
+ };
+
+ // singular matrices
+ protected Fraction[][] singular = { {new Fraction(2),new Fraction(3)}, {new Fraction(2),new Fraction(3)} };
+ protected Fraction[][] bigSingular = {
+ {new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)},
+ {new Fraction(2),new Fraction(5),new Fraction(3),new Fraction(4)},
+ {new Fraction(7),new Fraction(3),new Fraction(256),new Fraction(1930)},
+ {new Fraction(3),new Fraction(7),new Fraction(6),new Fraction(8)}
+ }; // 4th row = 1st + 2nd
+ protected Fraction[][] detData = {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(4),new Fraction(5),new Fraction(6)},
+ {new Fraction(7),new Fraction(8),new Fraction(10)}
+ };
+ protected Fraction[][] detData2 = { {new Fraction(1), new Fraction(3)}, {new Fraction(2), new Fraction(4)}};
+
+ // vectors
+ protected Fraction[] testVector = {new Fraction(1),new Fraction(2),new Fraction(3)};
+ protected Fraction[] testVector2 = {new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)};
+
+ // submatrix accessor tests
+ protected Fraction[][] subTestData = {
+ {new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4)},
+ {new Fraction(3, 2), new Fraction(5, 2), new Fraction(7, 2), new Fraction(9, 2)},
+ {new Fraction(2), new Fraction(4), new Fraction(6), new Fraction(8)},
+ {new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7)}
+ };
+ // array selections
+ protected Fraction[][] subRows02Cols13 = { {new Fraction(2), new Fraction(4)}, {new Fraction(4), new Fraction(8)}};
+ protected Fraction[][] subRows03Cols12 = { {new Fraction(2), new Fraction(3)}, {new Fraction(5), new Fraction(6)}};
+ protected Fraction[][] subRows03Cols123 = {
+ {new Fraction(2), new Fraction(3), new Fraction(4)},
+ {new Fraction(5), new Fraction(6), new Fraction(7)}
+ };
+ // effective permutations
+ protected Fraction[][] subRows20Cols123 = {
+ {new Fraction(4), new Fraction(6), new Fraction(8)},
+ {new Fraction(2), new Fraction(3), new Fraction(4)}
+ };
+ protected Fraction[][] subRows31Cols31 = {{new Fraction(7), new Fraction(5)}, {new Fraction(9, 2), new Fraction(5, 2)}};
+ // contiguous ranges
+ protected Fraction[][] subRows01Cols23 = {{new Fraction(3),new Fraction(4)} , {new Fraction(7, 2), new Fraction(9, 2)}};
+ protected Fraction[][] subRows23Cols00 = {{new Fraction(2)} , {new Fraction(4)}};
+ protected Fraction[][] subRows00Cols33 = {{new Fraction(4)}};
+ // row matrices
+ protected Fraction[][] subRow0 = {{new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)}};
+ protected Fraction[][] subRow3 = {{new Fraction(4),new Fraction(5),new Fraction(6),new Fraction(7)}};
+ // column matrices
+ protected Fraction[][] subColumn1 = {{new Fraction(2)}, {new Fraction(5, 2)}, {new Fraction(4)}, {new Fraction(5)}};
+ protected Fraction[][] subColumn3 = {{new Fraction(4)}, {new Fraction(9, 2)}, {new Fraction(8)}, {new Fraction(7)}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public BlockFieldMatrixTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> m2 = new BlockFieldMatrix<Fraction>(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ Random r = new Random(66636328996002l);
+ BlockFieldMatrix<Fraction> m1 = createRandomMatrix(r, 47, 83);
+ BlockFieldMatrix<Fraction> m2 = new BlockFieldMatrix<Fraction>(m1.getData());
+ assertEquals(m1, m2);
+ BlockFieldMatrix<Fraction> m3 = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> m4 = new BlockFieldMatrix<Fraction>(m3.getData());
+ assertEquals(m3, m4);
+ }
+
+ /** test add */
+ public void testAdd() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> mInv = new BlockFieldMatrix<Fraction>(testDataInv);
+ FieldMatrix<Fraction> mPlusMInv = m.add(mInv);
+ Fraction[][] sumEntries = mPlusMInv.getData();
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals(testDataPlusInv[row][col],sumEntries[row][col]);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> m2 = new BlockFieldMatrix<Fraction>(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> m2 = new BlockFieldMatrix<Fraction>(testDataInv);
+ TestUtils.assertEquals(m.subtract(m2), m2.scalarMultiply(new Fraction(-1)).add(m));
+ try {
+ m.subtract(new BlockFieldMatrix<Fraction>(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> mInv = new BlockFieldMatrix<Fraction>(testDataInv);
+ BlockFieldMatrix<Fraction> identity = new BlockFieldMatrix<Fraction>(id);
+ BlockFieldMatrix<Fraction> m2 = new BlockFieldMatrix<Fraction>(testData2);
+ TestUtils.assertEquals(m.multiply(mInv), identity);
+ TestUtils.assertEquals(mInv.multiply(m), identity);
+ TestUtils.assertEquals(m.multiply(identity), m);
+ TestUtils.assertEquals(identity.multiply(mInv), mInv);
+ TestUtils.assertEquals(m2.multiply(identity), m2);
+ try {
+ m.multiply(new BlockFieldMatrix<Fraction>(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testSeveralBlocks() {
+
+ FieldMatrix<Fraction> m =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), 37, 41);
+ for (int i = 0; i < m.getRowDimension(); ++i) {
+ for (int j = 0; j < m.getColumnDimension(); ++j) {
+ m.setEntry(i, j, new Fraction(i * 11 + j, 11));
+ }
+ }
+
+ FieldMatrix<Fraction> mT = m.transpose();
+ assertEquals(m.getRowDimension(), mT.getColumnDimension());
+ assertEquals(m.getColumnDimension(), mT.getRowDimension());
+ for (int i = 0; i < mT.getRowDimension(); ++i) {
+ for (int j = 0; j < mT.getColumnDimension(); ++j) {
+ assertEquals(m.getEntry(j, i), mT.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> mPm = m.add(m);
+ for (int i = 0; i < mPm.getRowDimension(); ++i) {
+ for (int j = 0; j < mPm.getColumnDimension(); ++j) {
+ assertEquals(m.getEntry(i, j).multiply(new Fraction(2)), mPm.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> mPmMm = mPm.subtract(m);
+ for (int i = 0; i < mPmMm.getRowDimension(); ++i) {
+ for (int j = 0; j < mPmMm.getColumnDimension(); ++j) {
+ assertEquals(m.getEntry(i, j), mPmMm.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> mTm = mT.multiply(m);
+ for (int i = 0; i < mTm.getRowDimension(); ++i) {
+ for (int j = 0; j < mTm.getColumnDimension(); ++j) {
+ Fraction sum = Fraction.ZERO;
+ for (int k = 0; k < mT.getColumnDimension(); ++k) {
+ sum = sum.add(new Fraction(k * 11 + i, 11).multiply(new Fraction(k * 11 + j, 11)));
+ }
+ assertEquals(sum, mTm.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> mmT = m.multiply(mT);
+ for (int i = 0; i < mmT.getRowDimension(); ++i) {
+ for (int j = 0; j < mmT.getColumnDimension(); ++j) {
+ Fraction sum = Fraction.ZERO;
+ for (int k = 0; k < m.getColumnDimension(); ++k) {
+ sum = sum.add(new Fraction(i * 11 + k, 11).multiply(new Fraction(j * 11 + k, 11)));
+ }
+ assertEquals(sum, mmT.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> sub1 = m.getSubMatrix(2, 9, 5, 20);
+ for (int i = 0; i < sub1.getRowDimension(); ++i) {
+ for (int j = 0; j < sub1.getColumnDimension(); ++j) {
+ assertEquals(new Fraction((i + 2) * 11 + (j + 5), 11), sub1.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> sub2 = m.getSubMatrix(10, 12, 3, 40);
+ for (int i = 0; i < sub2.getRowDimension(); ++i) {
+ for (int j = 0; j < sub2.getColumnDimension(); ++j) {
+ assertEquals(new Fraction((i + 10) * 11 + (j + 3), 11), sub2.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> sub3 = m.getSubMatrix(30, 34, 0, 5);
+ for (int i = 0; i < sub3.getRowDimension(); ++i) {
+ for (int j = 0; j < sub3.getColumnDimension(); ++j) {
+ assertEquals(new Fraction((i + 30) * 11 + (j + 0), 11), sub3.getEntry(i, j));
+ }
+ }
+
+ FieldMatrix<Fraction> sub4 = m.getSubMatrix(30, 32, 32, 35);
+ for (int i = 0; i < sub4.getRowDimension(); ++i) {
+ for (int j = 0; j < sub4.getColumnDimension(); ++j) {
+ assertEquals(new Fraction((i + 30) * 11 + (j + 32), 11), sub4.getEntry(i, j));
+ }
+ }
+
+ }
+
+ //Additional Test for BlockFieldMatrix<Fraction>Test.testMultiply
+
+ private Fraction[][] d3 = new Fraction[][] {
+ {new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)},
+ {new Fraction(5),new Fraction(6),new Fraction(7),new Fraction(8)}
+ };
+ private Fraction[][] d4 = new Fraction[][] {
+ {new Fraction(1)},
+ {new Fraction(2)},
+ {new Fraction(3)},
+ {new Fraction(4)}
+ };
+ private Fraction[][] d5 = new Fraction[][] {{new Fraction(30)},{new Fraction(70)}};
+
+ public void testMultiply2() {
+ FieldMatrix<Fraction> m3 = new BlockFieldMatrix<Fraction>(d3);
+ FieldMatrix<Fraction> m4 = new BlockFieldMatrix<Fraction>(d4);
+ FieldMatrix<Fraction> m5 = new BlockFieldMatrix<Fraction>(d5);
+ TestUtils.assertEquals(m3.multiply(m4), m5);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(id);
+ assertEquals(new Fraction(3),m.getTrace());
+ m = new BlockFieldMatrix<Fraction>(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test scalarAdd */
+ public void testScalarAdd() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(new BlockFieldMatrix<Fraction>(testDataPlus2),
+ m.scalarAdd(new Fraction(2)));
+ }
+
+ /** test operate */
+ public void testOperate() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(id);
+ TestUtils.assertEquals(testVector, m.operate(testVector));
+ TestUtils.assertEquals(testVector, m.operate(new ArrayFieldVector<Fraction>(testVector)).getData());
+ m = new BlockFieldMatrix<Fraction>(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testOperateLarge() {
+ int p = (11 * BlockFieldMatrix.BLOCK_SIZE) / 10;
+ int q = (11 * BlockFieldMatrix.BLOCK_SIZE) / 10;
+ int r = BlockFieldMatrix.BLOCK_SIZE / 2;
+ Random random = new Random(111007463902334l);
+ FieldMatrix<Fraction> m1 = createRandomMatrix(random, p, q);
+ FieldMatrix<Fraction> m2 = createRandomMatrix(random, q, r);
+ FieldMatrix<Fraction> m1m2 = m1.multiply(m2);
+ for (int i = 0; i < r; ++i) {
+ TestUtils.assertEquals(m1m2.getColumn(i), m1.operate(m2.getColumn(i)));
+ }
+ }
+
+ public void testOperatePremultiplyLarge() {
+ int p = (11 * BlockFieldMatrix.BLOCK_SIZE) / 10;
+ int q = (11 * BlockFieldMatrix.BLOCK_SIZE) / 10;
+ int r = BlockFieldMatrix.BLOCK_SIZE / 2;
+ Random random = new Random(111007463902334l);
+ FieldMatrix<Fraction> m1 = createRandomMatrix(random, p, q);
+ FieldMatrix<Fraction> m2 = createRandomMatrix(random, q, r);
+ FieldMatrix<Fraction> m1m2 = m1.multiply(m2);
+ for (int i = 0; i < p; ++i) {
+ TestUtils.assertEquals(m1m2.getRow(i), m2.preMultiply(m1.getRow(i)));
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ FieldMatrix<Fraction> a = new BlockFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(2) },
+ { new Fraction(3), new Fraction(4) },
+ { new Fraction(5), new Fraction(6) }
+ });
+ Fraction[] b = a.operate(new Fraction[] { new Fraction(1), new Fraction(1) });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( new Fraction(3), b[0]);
+ assertEquals( new Fraction(7), b[1]);
+ assertEquals(new Fraction(11), b[2]);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ FieldMatrix<Fraction> mIT = new FieldLUDecompositionImpl<Fraction>(m).getSolver().getInverse().transpose();
+ FieldMatrix<Fraction> mTI = new FieldLUDecompositionImpl<Fraction>(m.transpose()).getSolver().getInverse();
+ TestUtils.assertEquals(mIT, mTI);
+ m = new BlockFieldMatrix<Fraction>(testData2);
+ FieldMatrix<Fraction> mt = new BlockFieldMatrix<Fraction>(testData2T);
+ TestUtils.assertEquals(mt, m.transpose());
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(m.preMultiply(testVector), preMultTest);
+ TestUtils.assertEquals(m.preMultiply(new ArrayFieldVector<Fraction>(testVector).getData()),
+ preMultTest);
+ m = new BlockFieldMatrix<Fraction>(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ FieldMatrix<Fraction> m3 = new BlockFieldMatrix<Fraction>(d3);
+ FieldMatrix<Fraction> m4 = new BlockFieldMatrix<Fraction>(d4);
+ FieldMatrix<Fraction> m5 = new BlockFieldMatrix<Fraction>(d5);
+ TestUtils.assertEquals(m4.preMultiply(m3), m5);
+
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> mInv = new BlockFieldMatrix<Fraction>(testDataInv);
+ BlockFieldMatrix<Fraction> identity = new BlockFieldMatrix<Fraction>(id);
+ TestUtils.assertEquals(m.preMultiply(mInv), identity);
+ TestUtils.assertEquals(mInv.preMultiply(m), identity);
+ TestUtils.assertEquals(m.preMultiply(identity), m);
+ TestUtils.assertEquals(identity.preMultiply(mInv), mInv);
+ try {
+ m.preMultiply(new BlockFieldMatrix<Fraction>(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(m.getRow(0), testDataRow1);
+ TestUtils.assertEquals(m.getColumn(2), testDataCol3);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ assertEquals(m.getEntry(0,1),new Fraction(2));
+ try {
+ m.getEntry(10, 4);
+ fail ("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ Fraction[][] matrixData = {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(2),new Fraction(5),new Fraction(3)}
+ };
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(matrixData);
+ // One more with three rows, two columns
+ Fraction[][] matrixData2 = {
+ {new Fraction(1),new Fraction(2)},
+ {new Fraction(2),new Fraction(5)},
+ {new Fraction(1), new Fraction(7)}
+ };
+ FieldMatrix<Fraction> n = new BlockFieldMatrix<Fraction>(matrixData2);
+ // Now multiply m by n
+ FieldMatrix<Fraction> p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ FieldMatrix<Fraction> pInverse = new FieldLUDecompositionImpl<Fraction>(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ Fraction[][] coefficientsData = {
+ {new Fraction(2), new Fraction(3), new Fraction(-2)},
+ {new Fraction(-1), new Fraction(7), new Fraction(6)},
+ {new Fraction(4), new Fraction(-3), new Fraction(-5)}
+ };
+ FieldMatrix<Fraction> coefficients = new BlockFieldMatrix<Fraction>(coefficientsData);
+ Fraction[] constants = {new Fraction(1), new Fraction(-2), new Fraction(1)};
+ Fraction[] solution = new FieldLUDecompositionImpl<Fraction>(coefficients).getSolver().solve(constants);
+ assertEquals(new Fraction(2).multiply(solution[0]).
+ add(new Fraction(3).multiply(solution[1])).
+ subtract(new Fraction(2).multiply(solution[2])),
+ constants[0]);
+ assertEquals(new Fraction(-1).multiply(solution[0]).
+ add(new Fraction(7).multiply(solution[1])).
+ add(new Fraction(6).multiply(solution[2])),
+ constants[1]);
+ assertEquals(new Fraction(4).multiply(solution[0]).
+ subtract(new Fraction(3).multiply(solution[1])).
+ subtract(new Fraction(5).multiply(solution[2])),
+ constants[2]);
+
+ }
+
+ // test submatrix accessors
+ public void testGetSubMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ checkGetSubMatrix(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkGetSubMatrix(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkGetSubMatrix(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkGetSubMatrix(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkGetSubMatrix(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkGetSubMatrix(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, -1, 1, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, new int[] {}, new int[] { 0 });
+ checkGetSubMatrix(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkGetSubMatrix(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ FieldMatrix<Fraction> sub = m.getSubMatrix(startRow, endRow, startColumn, endColumn);
+ if (reference != null) {
+ assertEquals(new BlockFieldMatrix<Fraction>(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkGetSubMatrix(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ FieldMatrix<Fraction> sub = m.getSubMatrix(selectedRows, selectedColumns);
+ if (reference != null) {
+ assertEquals(new BlockFieldMatrix<Fraction>(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetSetMatrixLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ FieldMatrix<Fraction> sub =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n - 4, n - 4).scalarAdd(new Fraction(1));
+
+ m.setSubMatrix(sub.getData(), 2, 2);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if ((i < 2) || (i > n - 3) || (j < 2) || (j > n - 3)) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ assertEquals(sub, m.getSubMatrix(2, n - 3, 2, n - 3));
+
+ }
+
+ public void testCopySubMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ checkCopy(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkCopy(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkCopy(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkCopy(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkCopy(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkCopy(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, -1, 1, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, new int[] {}, new int[] { 0 });
+ checkCopy(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkCopy(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ Fraction[][] sub = (reference == null) ?
+ new Fraction[1][1] :
+ new Fraction[reference.length][reference[0].length];
+ m.copySubMatrix(startRow, endRow, startColumn, endColumn, sub);
+ if (reference != null) {
+ assertEquals(new BlockFieldMatrix<Fraction>(reference), new BlockFieldMatrix<Fraction>(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkCopy(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ Fraction[][] sub = (reference == null) ?
+ new Fraction[1][1] :
+ new Fraction[reference.length][reference[0].length];
+ m.copySubMatrix(selectedRows, selectedColumns, sub);
+ if (reference != null) {
+ assertEquals(new BlockFieldMatrix<Fraction>(reference), new BlockFieldMatrix<Fraction>(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetRowMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mRow0 = new BlockFieldMatrix<Fraction>(subRow0);
+ FieldMatrix<Fraction> mRow3 = new BlockFieldMatrix<Fraction>(subRow3);
+ assertEquals("Row0", mRow0, m.getRowMatrix(0));
+ assertEquals("Row3", mRow3, m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mRow3 = new BlockFieldMatrix<Fraction>(subRow3);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowMatrix(0, mRow3);
+ assertEquals(mRow3, m.getRowMatrix(0));
+ try {
+ m.setRowMatrix(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowMatrixLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ FieldMatrix<Fraction> sub =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), 1, n).scalarAdd(new Fraction(1));
+
+ m.setRowMatrix(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ assertEquals(sub, m.getRowMatrix(2));
+
+ }
+
+ public void testGetColumnMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mColumn1 = new BlockFieldMatrix<Fraction>(subColumn1);
+ FieldMatrix<Fraction> mColumn3 = new BlockFieldMatrix<Fraction>(subColumn3);
+ assertEquals(mColumn1, m.getColumnMatrix(1));
+ assertEquals(mColumn3, m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnMatrix() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mColumn3 = new BlockFieldMatrix<Fraction>(subColumn3);
+ assertNotSame(mColumn3, m.getColumnMatrix(1));
+ m.setColumnMatrix(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnMatrix(1));
+ try {
+ m.setColumnMatrix(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnMatrixLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ FieldMatrix<Fraction> sub =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, 1).scalarAdd(new Fraction(1));
+
+ m.setColumnMatrix(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ assertEquals(sub, m.getColumnMatrix(2));
+
+ }
+
+ public void testGetRowVector() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mRow0 = new ArrayFieldVector<Fraction>(subRow0[0]);
+ FieldVector<Fraction> mRow3 = new ArrayFieldVector<Fraction>(subRow3[0]);
+ assertEquals(mRow0, m.getRowVector(0));
+ assertEquals(mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowVector() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mRow3 = new ArrayFieldVector<Fraction>(subRow3[0]);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowVector(0, mRow3);
+ assertEquals(mRow3, m.getRowVector(0));
+ try {
+ m.setRowVector(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowVector(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), 5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowVectorLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ FieldVector<Fraction> sub = new ArrayFieldVector<Fraction>(n, new Fraction(1));
+
+ m.setRowVector(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ assertEquals(sub, m.getRowVector(2));
+
+ }
+
+ public void testGetColumnVector() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mColumn1 = columnToVector(subColumn1);
+ FieldVector<Fraction> mColumn3 = columnToVector(subColumn3);
+ assertEquals(mColumn1, m.getColumnVector(1));
+ assertEquals(mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnVector() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mColumn3 = columnToVector(subColumn3);
+ assertNotSame(mColumn3, m.getColumnVector(1));
+ m.setColumnVector(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnVector(1));
+ try {
+ m.setColumnVector(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnVector(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), 5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnVectorLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ FieldVector<Fraction> sub = new ArrayFieldVector<Fraction>(n, new Fraction(1));
+
+ m.setColumnVector(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ assertEquals(sub, m.getColumnVector(2));
+
+ }
+
+ private FieldVector<Fraction> columnToVector(Fraction[][] column) {
+ Fraction[] data = new Fraction[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayFieldVector<Fraction>(data, false);
+ }
+
+ public void testGetRow() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ checkArrays(subRow0[0], m.getRow(0));
+ checkArrays(subRow3[0], m.getRow(3));
+ try {
+ m.getRow(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRow(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRow() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ assertTrue(subRow3[0][0] != m.getRow(0)[0]);
+ m.setRow(0, subRow3[0]);
+ checkArrays(subRow3[0], m.getRow(0));
+ try {
+ m.setRow(-1, subRow3[0]);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRow(0, new Fraction[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ Fraction[] sub = new Fraction[n];
+ Arrays.fill(sub, new Fraction(1));
+
+ m.setRow(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ checkArrays(sub, m.getRow(2));
+
+ }
+
+ public void testGetColumn() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ Fraction[] mColumn1 = columnToArray(subColumn1);
+ Fraction[] mColumn3 = columnToArray(subColumn3);
+ checkArrays(mColumn1, m.getColumn(1));
+ checkArrays(mColumn3, m.getColumn(3));
+ try {
+ m.getColumn(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumn(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumn() {
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(subTestData);
+ Fraction[] mColumn3 = columnToArray(subColumn3);
+ assertTrue(mColumn3[0] != m.getColumn(1)[0]);
+ m.setColumn(1, mColumn3);
+ checkArrays(mColumn3, m.getColumn(1));
+ try {
+ m.setColumn(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumn(0, new Fraction[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnLarge() {
+ int n = 3 * BlockFieldMatrix.BLOCK_SIZE;
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), n, n);
+ Fraction[] sub = new Fraction[n];
+ Arrays.fill(sub, new Fraction(1));
+
+ m.setColumn(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(new Fraction(0), m.getEntry(i, j));
+ } else {
+ assertEquals(new Fraction(1), m.getEntry(i, j));
+ }
+ }
+ }
+ checkArrays(sub, m.getColumn(2));
+
+ }
+
+ private Fraction[] columnToArray(Fraction[][] column) {
+ Fraction[] data = new Fraction[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return data;
+ }
+
+ private void checkArrays(Fraction[] expected, Fraction[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ BlockFieldMatrix<Fraction> m1 = (BlockFieldMatrix<Fraction>) m.copy();
+ BlockFieldMatrix<Fraction> mt = (BlockFieldMatrix<Fraction>) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new BlockFieldMatrix<Fraction>(bigSingular)));
+ }
+
+ public void testToString() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ assertEquals("BlockFieldMatrix{{1,2,3},{2,5,3},{1,0,8}}", m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ m.setSubMatrix(detData2,1,1);
+ FieldMatrix<Fraction> expected = new BlockFieldMatrix<Fraction>
+ (new Fraction[][] {{new Fraction(1),new Fraction(2),new Fraction(3)},{new Fraction(2),new Fraction(1),new Fraction(3)},{new Fraction(1),new Fraction(2),new Fraction(4)}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2,0,0);
+ expected = new BlockFieldMatrix<Fraction>
+ (new Fraction[][] {{new Fraction(1),new Fraction(3),new Fraction(3)},{new Fraction(2),new Fraction(4),new Fraction(3)},{new Fraction(1),new Fraction(2),new Fraction(4)}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2,0,0);
+ expected = new BlockFieldMatrix<Fraction>
+ (new Fraction[][] {{new Fraction(3),new Fraction(4),new Fraction(5)},{new Fraction(4),new Fraction(7),new Fraction(5)},{new Fraction(3),new Fraction(2),new Fraction(10)}});
+ assertEquals(expected, m);
+
+ // javadoc example
+ BlockFieldMatrix<Fraction> matrix =
+ new BlockFieldMatrix<Fraction>(new Fraction[][] {
+ {new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4)},
+ {new Fraction(5), new Fraction(6), new Fraction(7), new Fraction(8)},
+ {new Fraction(9), new Fraction(0), new Fraction(1) , new Fraction(2)}
+ });
+ matrix.setSubMatrix(new Fraction[][] {
+ {new Fraction(3), new Fraction(4)},
+ {new Fraction(5), new Fraction(6)}
+ }, 1, 1);
+ expected =
+ new BlockFieldMatrix<Fraction>(new Fraction[][] {
+ {new Fraction(1), new Fraction(2), new Fraction(3),new Fraction(4)},
+ {new Fraction(5), new Fraction(3), new Fraction(4), new Fraction(8)},
+ {new Fraction(9), new Fraction(5) ,new Fraction(6), new Fraction(2)}
+ });
+ assertEquals(expected, matrix);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData,1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData,-1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData,1,-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new Fraction[][] {{new Fraction(1)}, {new Fraction(2), new Fraction(3)}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new Fraction[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testWalk() throws MatrixVisitorException {
+ int rows = 150;
+ int columns = 75;
+
+ FieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInRowOrder(new SetVisitor());
+ GetVisitor getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInRowOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInColumnOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInColumnOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ }
+
+ public void testSerial() {
+ BlockFieldMatrix<Fraction> m = new BlockFieldMatrix<Fraction>(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+ private static class SetVisitor extends DefaultFieldMatrixChangingVisitor<Fraction> {
+ public SetVisitor() {
+ super(Fraction.ZERO);
+ }
+ @Override
+ public Fraction visit(int i, int j, Fraction value) {
+ return new Fraction(i * 11 + j, 11);
+ }
+ }
+
+ private static class GetVisitor extends DefaultFieldMatrixPreservingVisitor<Fraction> {
+ private int count;
+ public GetVisitor() {
+ super(Fraction.ZERO);
+ count = 0;
+ }
+ @Override
+ public void visit(int i, int j, Fraction value) {
+ ++count;
+ assertEquals(new Fraction(i * 11 + j, 11), value);
+ }
+ public int getCount() {
+ return count;
+ }
+ }
+
+ private BlockFieldMatrix<Fraction> createRandomMatrix(Random r, int rows, int columns) {
+ BlockFieldMatrix<Fraction> m =
+ new BlockFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ for (int i = 0; i < rows; ++i) {
+ for (int j = 0; j < columns; ++j) {
+ int p = r.nextInt(20) - 10;
+ int q = r.nextInt(20) - 10;
+ if (q == 0) {
+ q = 1;
+ }
+ m.setEntry(i, j, new Fraction(p, q));
+ }
+ }
+ return m;
+ }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/BlockRealMatrixTest.java b/src/test/java/org/apache/commons/math/linear/BlockRealMatrixTest.java
new file mode 100644
index 0000000..d572c10
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/BlockRealMatrixTest.java
@@ -0,0 +1,1204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link BlockRealMatrix} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+
+public final class BlockRealMatrixTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected double[][] id = { {1d,0d,0d}, {0d,1d,0d}, {0d,0d,1d} };
+
+ // Test data for group operations
+ protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
+ protected double[][] testDataLU = {{2d, 5d, 3d}, {.5d, -2.5d, 6.5d}, {0.5d, 0.2d, .2d}};
+ protected double[][] testDataPlus2 = { {3d,4d,5d}, {4d,7d,5d}, {3d,2d,10d} };
+ protected double[][] testDataMinus = { {-1d,-2d,-3d}, {-2d,-5d,-3d},
+ {-1d,0d,-8d} };
+ protected double[] testDataRow1 = {1d,2d,3d};
+ protected double[] testDataCol3 = {3d,3d,8d};
+ protected double[][] testDataInv =
+ { {-40d,16d,9d}, {13d,-5d,-3d}, {5d,-2d,-1d} };
+ protected double[] preMultTest = {8,12,33};
+ protected double[][] testData2 ={ {1d,2d,3d}, {2d,5d,3d}};
+ protected double[][] testData2T = { {1d,2d}, {2d,5d}, {3d,3d}};
+ protected double[][] testDataPlusInv =
+ { {-39d,18d,12d}, {15d,0d,0d}, {6d,-2d,7d} };
+
+ // lu decomposition tests
+ protected double[][] luData = { {2d,3d,3d}, {0d,5d,7d}, {6d,9d,8d} };
+ protected double[][] luDataLUDecomposition = { {6d,9d,8d}, {0d,5d,7d},
+ {0.33333333333333,0d,0.33333333333333} };
+
+ // singular matrices
+ protected double[][] singular = { {2d,3d}, {2d,3d} };
+ protected double[][] bigSingular = {{1d,2d,3d,4d}, {2d,5d,3d,4d},
+ {7d,3d,256d,1930d}, {3d,7d,6d,8d}}; // 4th row = 1st + 2nd
+ protected double[][] detData = { {1d,2d,3d}, {4d,5d,6d}, {7d,8d,10d} };
+ protected double[][] detData2 = { {1d, 3d}, {2d, 4d}};
+
+ // vectors
+ protected double[] testVector = {1,2,3};
+ protected double[] testVector2 = {1,2,3,4};
+
+ // submatrix accessor tests
+ protected double[][] subTestData = {{1, 2, 3, 4}, {1.5, 2.5, 3.5, 4.5},
+ {2, 4, 6, 8}, {4, 5, 6, 7}};
+ // array selections
+ protected double[][] subRows02Cols13 = { {2, 4}, {4, 8}};
+ protected double[][] subRows03Cols12 = { {2, 3}, {5, 6}};
+ protected double[][] subRows03Cols123 = { {2, 3, 4} , {5, 6, 7}};
+ // effective permutations
+ protected double[][] subRows20Cols123 = { {4, 6, 8} , {2, 3, 4}};
+ protected double[][] subRows31Cols31 = {{7, 5}, {4.5, 2.5}};
+ // contiguous ranges
+ protected double[][] subRows01Cols23 = {{3,4} , {3.5, 4.5}};
+ protected double[][] subRows23Cols00 = {{2} , {4}};
+ protected double[][] subRows00Cols33 = {{4}};
+ // row matrices
+ protected double[][] subRow0 = {{1,2,3,4}};
+ protected double[][] subRow3 = {{4,5,6,7}};
+ // column matrices
+ protected double[][] subColumn1 = {{2}, {2.5}, {4}, {5}};
+ protected double[][] subColumn3 = {{4}, {4.5}, {8}, {7}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public BlockRealMatrixTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m2 = new BlockRealMatrix(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ Random r = new Random(66636328996002l);
+ BlockRealMatrix m1 = createRandomMatrix(r, 47, 83);
+ BlockRealMatrix m2 = new BlockRealMatrix(m1.getData());
+ assertEquals(m1, m2);
+ BlockRealMatrix m3 = new BlockRealMatrix(testData);
+ BlockRealMatrix m4 = new BlockRealMatrix(m3.getData());
+ assertEquals(m3, m4);
+ }
+
+ /** test add */
+ public void testAdd() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix mInv = new BlockRealMatrix(testDataInv);
+ RealMatrix mPlusMInv = m.add(mInv);
+ double[][] sumEntries = mPlusMInv.getData();
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ testDataPlusInv[row][col],sumEntries[row][col],
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m2 = new BlockRealMatrix(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test norm */
+ public void testNorm() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m2 = new BlockRealMatrix(testData2);
+ assertEquals("testData norm",14d,m.getNorm(),entryTolerance);
+ assertEquals("testData2 norm",7d,m2.getNorm(),entryTolerance);
+ }
+
+ /** test Frobenius norm */
+ public void testFrobeniusNorm() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m2 = new BlockRealMatrix(testData2);
+ assertEquals("testData Frobenius norm", FastMath.sqrt(117.0), m.getFrobeniusNorm(), entryTolerance);
+ assertEquals("testData2 Frobenius norm", FastMath.sqrt(52.0), m2.getFrobeniusNorm(), entryTolerance);
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m2 = new BlockRealMatrix(testDataInv);
+ assertClose(m.subtract(m2), m2.scalarMultiply(-1d).add(m), entryTolerance);
+ try {
+ m.subtract(new BlockRealMatrix(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix mInv = new BlockRealMatrix(testDataInv);
+ BlockRealMatrix identity = new BlockRealMatrix(id);
+ BlockRealMatrix m2 = new BlockRealMatrix(testData2);
+ assertClose(m.multiply(mInv), identity, entryTolerance);
+ assertClose(mInv.multiply(m), identity, entryTolerance);
+ assertClose(m.multiply(identity), m, entryTolerance);
+ assertClose(identity.multiply(mInv), mInv, entryTolerance);
+ assertClose(m2.multiply(identity), m2, entryTolerance);
+ try {
+ m.multiply(new BlockRealMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testSeveralBlocks() {
+
+ RealMatrix m = new BlockRealMatrix(35, 71);
+ for (int i = 0; i < m.getRowDimension(); ++i) {
+ for (int j = 0; j < m.getColumnDimension(); ++j) {
+ m.setEntry(i, j, i + j / 1024.0);
+ }
+ }
+
+ RealMatrix mT = m.transpose();
+ assertEquals(m.getRowDimension(), mT.getColumnDimension());
+ assertEquals(m.getColumnDimension(), mT.getRowDimension());
+ for (int i = 0; i < mT.getRowDimension(); ++i) {
+ for (int j = 0; j < mT.getColumnDimension(); ++j) {
+ assertEquals(m.getEntry(j, i), mT.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix mPm = m.add(m);
+ for (int i = 0; i < mPm.getRowDimension(); ++i) {
+ for (int j = 0; j < mPm.getColumnDimension(); ++j) {
+ assertEquals(2 * m.getEntry(i, j), mPm.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix mPmMm = mPm.subtract(m);
+ for (int i = 0; i < mPmMm.getRowDimension(); ++i) {
+ for (int j = 0; j < mPmMm.getColumnDimension(); ++j) {
+ assertEquals(m.getEntry(i, j), mPmMm.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix mTm = mT.multiply(m);
+ for (int i = 0; i < mTm.getRowDimension(); ++i) {
+ for (int j = 0; j < mTm.getColumnDimension(); ++j) {
+ double sum = 0;
+ for (int k = 0; k < mT.getColumnDimension(); ++k) {
+ sum += (k + i / 1024.0) * (k + j / 1024.0);
+ }
+ assertEquals(sum, mTm.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix mmT = m.multiply(mT);
+ for (int i = 0; i < mmT.getRowDimension(); ++i) {
+ for (int j = 0; j < mmT.getColumnDimension(); ++j) {
+ double sum = 0;
+ for (int k = 0; k < m.getColumnDimension(); ++k) {
+ sum += (i + k / 1024.0) * (j + k / 1024.0);
+ }
+ assertEquals(sum, mmT.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix sub1 = m.getSubMatrix(2, 9, 5, 20);
+ for (int i = 0; i < sub1.getRowDimension(); ++i) {
+ for (int j = 0; j < sub1.getColumnDimension(); ++j) {
+ assertEquals((i + 2) + (j + 5) / 1024.0, sub1.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix sub2 = m.getSubMatrix(10, 12, 3, 70);
+ for (int i = 0; i < sub2.getRowDimension(); ++i) {
+ for (int j = 0; j < sub2.getColumnDimension(); ++j) {
+ assertEquals((i + 10) + (j + 3) / 1024.0, sub2.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix sub3 = m.getSubMatrix(30, 34, 0, 5);
+ for (int i = 0; i < sub3.getRowDimension(); ++i) {
+ for (int j = 0; j < sub3.getColumnDimension(); ++j) {
+ assertEquals((i + 30) + (j + 0) / 1024.0, sub3.getEntry(i, j), 0);
+ }
+ }
+
+ RealMatrix sub4 = m.getSubMatrix(30, 32, 62, 65);
+ for (int i = 0; i < sub4.getRowDimension(); ++i) {
+ for (int j = 0; j < sub4.getColumnDimension(); ++j) {
+ assertEquals((i + 30) + (j + 62) / 1024.0, sub4.getEntry(i, j), 0);
+ }
+ }
+
+ }
+
+ //Additional Test for BlockRealMatrixTest.testMultiply
+
+ private double[][] d3 = new double[][] {{1,2,3,4},{5,6,7,8}};
+ private double[][] d4 = new double[][] {{1},{2},{3},{4}};
+ private double[][] d5 = new double[][] {{30},{70}};
+
+ public void testMultiply2() {
+ RealMatrix m3 = new BlockRealMatrix(d3);
+ RealMatrix m4 = new BlockRealMatrix(d4);
+ RealMatrix m5 = new BlockRealMatrix(d5);
+ assertClose(m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ RealMatrix m = new BlockRealMatrix(id);
+ assertEquals("identity trace",3d,m.getTrace(),entryTolerance);
+ m = new BlockRealMatrix(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test scalarAdd */
+ public void testScalarAdd() {
+ RealMatrix m = new BlockRealMatrix(testData);
+ assertClose(new BlockRealMatrix(testDataPlus2), m.scalarAdd(2d), entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ RealMatrix m = new BlockRealMatrix(id);
+ assertClose(testVector, m.operate(testVector), entryTolerance);
+ assertClose(testVector, m.operate(new ArrayRealVector(testVector)).getData(), entryTolerance);
+ m = new BlockRealMatrix(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testOperateLarge() {
+ int p = (7 * BlockRealMatrix.BLOCK_SIZE) / 2;
+ int q = (5 * BlockRealMatrix.BLOCK_SIZE) / 2;
+ int r = 3 * BlockRealMatrix.BLOCK_SIZE;
+ Random random = new Random(111007463902334l);
+ RealMatrix m1 = createRandomMatrix(random, p, q);
+ RealMatrix m2 = createRandomMatrix(random, q, r);
+ RealMatrix m1m2 = m1.multiply(m2);
+ for (int i = 0; i < r; ++i) {
+ checkArrays(m1m2.getColumn(i), m1.operate(m2.getColumn(i)));
+ }
+ }
+
+ public void testOperatePremultiplyLarge() {
+ int p = (7 * BlockRealMatrix.BLOCK_SIZE) / 2;
+ int q = (5 * BlockRealMatrix.BLOCK_SIZE) / 2;
+ int r = 3 * BlockRealMatrix.BLOCK_SIZE;
+ Random random = new Random(111007463902334l);
+ RealMatrix m1 = createRandomMatrix(random, p, q);
+ RealMatrix m2 = createRandomMatrix(random, q, r);
+ RealMatrix m1m2 = m1.multiply(m2);
+ for (int i = 0; i < p; ++i) {
+ checkArrays(m1m2.getRow(i), m2.preMultiply(m1.getRow(i)));
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ RealMatrix a = new BlockRealMatrix(new double[][] {
+ { 1, 2 }, { 3, 4 }, { 5, 6 }
+ });
+ double[] b = a.operate(new double[] { 1, 1 });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( 3.0, b[0], 1.0e-12);
+ assertEquals( 7.0, b[1], 1.0e-12);
+ assertEquals(11.0, b[2], 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ RealMatrix m = new BlockRealMatrix(testData);
+ RealMatrix mIT = new LUDecompositionImpl(m).getSolver().getInverse().transpose();
+ RealMatrix mTI = new LUDecompositionImpl(m.transpose()).getSolver().getInverse();
+ assertClose(mIT, mTI, normTolerance);
+ m = new BlockRealMatrix(testData2);
+ RealMatrix mt = new BlockRealMatrix(testData2T);
+ assertClose(mt, m.transpose(), normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ RealMatrix m = new BlockRealMatrix(testData);
+ assertClose(m.preMultiply(testVector), preMultTest, normTolerance);
+ assertClose(m.preMultiply(new ArrayRealVector(testVector).getData()),
+ preMultTest, normTolerance);
+ m = new BlockRealMatrix(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ RealMatrix m3 = new BlockRealMatrix(d3);
+ RealMatrix m4 = new BlockRealMatrix(d4);
+ RealMatrix m5 = new BlockRealMatrix(d5);
+ assertClose(m4.preMultiply(m3), m5, entryTolerance);
+
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix mInv = new BlockRealMatrix(testDataInv);
+ BlockRealMatrix identity = new BlockRealMatrix(id);
+ assertClose(m.preMultiply(mInv), identity, entryTolerance);
+ assertClose(mInv.preMultiply(m), identity, entryTolerance);
+ assertClose(m.preMultiply(identity), m, entryTolerance);
+ assertClose(identity.preMultiply(mInv), mInv, entryTolerance);
+ try {
+ m.preMultiply(new BlockRealMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ RealMatrix m = new BlockRealMatrix(testData);
+ assertClose(m.getRow(0), testDataRow1, entryTolerance);
+ assertClose(m.getColumn(2), testDataCol3, entryTolerance);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ RealMatrix m = new BlockRealMatrix(testData);
+ assertEquals("get entry",m.getEntry(0,1),2d,entryTolerance);
+ try {
+ m.getEntry(10, 4);
+ fail ("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ double[][] matrixData = { {1d,2d,3d}, {2d,5d,3d}};
+ RealMatrix m = new BlockRealMatrix(matrixData);
+ // One more with three rows, two columns
+ double[][] matrixData2 = { {1d,2d}, {2d,5d}, {1d, 7d}};
+ RealMatrix n = new BlockRealMatrix(matrixData2);
+ // Now multiply m by n
+ RealMatrix p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ RealMatrix pInverse = new LUDecompositionImpl(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ double[][] coefficientsData = {{2, 3, -2}, {-1, 7, 6}, {4, -3, -5}};
+ RealMatrix coefficients = new BlockRealMatrix(coefficientsData);
+ double[] constants = {1, -2, 1};
+ double[] solution = new LUDecompositionImpl(coefficients).getSolver().solve(constants);
+ assertEquals(2 * solution[0] + 3 * solution[1] -2 * solution[2], constants[0], 1E-12);
+ assertEquals(-1 * solution[0] + 7 * solution[1] + 6 * solution[2], constants[1], 1E-12);
+ assertEquals(4 * solution[0] - 3 * solution[1] -5 * solution[2], constants[2], 1E-12);
+
+ }
+
+ // test submatrix accessors
+ public void testGetSubMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ checkGetSubMatrix(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkGetSubMatrix(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkGetSubMatrix(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkGetSubMatrix(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkGetSubMatrix(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkGetSubMatrix(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, -1, 1, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, new int[] {}, new int[] { 0 });
+ checkGetSubMatrix(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ RealMatrix sub = m.getSubMatrix(startRow, endRow, startColumn, endColumn);
+ if (reference != null) {
+ assertEquals(new BlockRealMatrix(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ RealMatrix sub = m.getSubMatrix(selectedRows, selectedColumns);
+ if (reference != null) {
+ assertEquals(new BlockRealMatrix(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetSetMatrixLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ RealMatrix sub = new BlockRealMatrix(n - 4, n - 4).scalarAdd(1);
+
+ m.setSubMatrix(sub.getData(), 2, 2);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if ((i < 2) || (i > n - 3) || (j < 2) || (j > n - 3)) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ assertEquals(sub, m.getSubMatrix(2, n - 3, 2, n - 3));
+
+ }
+
+ public void testCopySubMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ checkCopy(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkCopy(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkCopy(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkCopy(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkCopy(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkCopy(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, -1, 1, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, new int[] {}, new int[] { 0 });
+ checkCopy(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(startRow, endRow, startColumn, endColumn, sub);
+ if (reference != null) {
+ assertEquals(new BlockRealMatrix(reference), new BlockRealMatrix(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(selectedRows, selectedColumns, sub);
+ if (reference != null) {
+ assertEquals(new BlockRealMatrix(reference), new BlockRealMatrix(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetRowMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealMatrix mRow0 = new BlockRealMatrix(subRow0);
+ RealMatrix mRow3 = new BlockRealMatrix(subRow3);
+ assertEquals("Row0", mRow0, m.getRowMatrix(0));
+ assertEquals("Row3", mRow3, m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealMatrix mRow3 = new BlockRealMatrix(subRow3);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowMatrix(0, mRow3);
+ assertEquals(mRow3, m.getRowMatrix(0));
+ try {
+ m.setRowMatrix(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowMatrixLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ RealMatrix sub = new BlockRealMatrix(1, n).scalarAdd(1);
+
+ m.setRowMatrix(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ assertEquals(sub, m.getRowMatrix(2));
+
+ }
+
+ public void testGetColumnMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealMatrix mColumn1 = new BlockRealMatrix(subColumn1);
+ RealMatrix mColumn3 = new BlockRealMatrix(subColumn3);
+ assertEquals(mColumn1, m.getColumnMatrix(1));
+ assertEquals(mColumn3, m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnMatrix() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealMatrix mColumn3 = new BlockRealMatrix(subColumn3);
+ assertNotSame(mColumn3, m.getColumnMatrix(1));
+ m.setColumnMatrix(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnMatrix(1));
+ try {
+ m.setColumnMatrix(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnMatrixLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ RealMatrix sub = new BlockRealMatrix(n, 1).scalarAdd(1);
+
+ m.setColumnMatrix(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ assertEquals(sub, m.getColumnMatrix(2));
+
+ }
+
+ public void testGetRowVector() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealVector mRow0 = new ArrayRealVector(subRow0[0]);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertEquals(mRow0, m.getRowVector(0));
+ assertEquals(mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowVector() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowVector(0, mRow3);
+ assertEquals(mRow3, m.getRowVector(0));
+ try {
+ m.setRowVector(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowVectorLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ RealVector sub = new ArrayRealVector(n, 1.0);
+
+ m.setRowVector(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ assertEquals(sub, m.getRowVector(2));
+
+ }
+
+ public void testGetColumnVector() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealVector mColumn1 = columnToVector(subColumn1);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertEquals(mColumn1, m.getColumnVector(1));
+ assertEquals(mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnVector() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertNotSame(mColumn3, m.getColumnVector(1));
+ m.setColumnVector(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnVector(1));
+ try {
+ m.setColumnVector(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnVectorLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ RealVector sub = new ArrayRealVector(n, 1.0);
+
+ m.setColumnVector(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ assertEquals(sub, m.getColumnVector(2));
+
+ }
+
+ private RealVector columnToVector(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayRealVector(data, false);
+ }
+
+ public void testGetRow() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ checkArrays(subRow0[0], m.getRow(0));
+ checkArrays(subRow3[0], m.getRow(3));
+ try {
+ m.getRow(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRow(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRow() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ assertTrue(subRow3[0][0] != m.getRow(0)[0]);
+ m.setRow(0, subRow3[0]);
+ checkArrays(subRow3[0], m.getRow(0));
+ try {
+ m.setRow(-1, subRow3[0]);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRow(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetRowLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ double[] sub = new double[n];
+ Arrays.fill(sub, 1.0);
+
+ m.setRow(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ checkArrays(sub, m.getRow(2));
+
+ }
+
+ public void testGetColumn() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ double[] mColumn1 = columnToArray(subColumn1);
+ double[] mColumn3 = columnToArray(subColumn3);
+ checkArrays(mColumn1, m.getColumn(1));
+ checkArrays(mColumn3, m.getColumn(3));
+ try {
+ m.getColumn(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumn(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumn() {
+ RealMatrix m = new BlockRealMatrix(subTestData);
+ double[] mColumn3 = columnToArray(subColumn3);
+ assertTrue(mColumn3[0] != m.getColumn(1)[0]);
+ m.setColumn(1, mColumn3);
+ checkArrays(mColumn3, m.getColumn(1));
+ try {
+ m.setColumn(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumn(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetSetColumnLarge() {
+ int n = 3 * BlockRealMatrix.BLOCK_SIZE;
+ RealMatrix m = new BlockRealMatrix(n, n);
+ double[] sub = new double[n];
+ Arrays.fill(sub, 1.0);
+
+ m.setColumn(2, sub);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (j != 2) {
+ assertEquals(0.0, m.getEntry(i, j), 0.0);
+ } else {
+ assertEquals(1.0, m.getEntry(i, j), 0.0);
+ }
+ }
+ }
+ checkArrays(sub, m.getColumn(2));
+
+ }
+
+ private double[] columnToArray(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return data;
+ }
+
+ private void checkArrays(double[] expected, double[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ BlockRealMatrix m1 = m.copy();
+ BlockRealMatrix mt = m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new BlockRealMatrix(bigSingular)));
+ }
+
+ public void testToString() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ assertEquals("BlockRealMatrix{{1.0,2.0,3.0},{2.0,5.0,3.0},{1.0,0.0,8.0}}",
+ m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ m.setSubMatrix(detData2,1,1);
+ RealMatrix expected = new BlockRealMatrix
+ (new double[][] {{1.0,2.0,3.0},{2.0,1.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2,0,0);
+ expected = new BlockRealMatrix
+ (new double[][] {{1.0,3.0,3.0},{2.0,4.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2,0,0);
+ expected = new BlockRealMatrix
+ (new double[][] {{3.0,4.0,5.0},{4.0,7.0,5.0},{3.0,2.0,10.0}});
+ assertEquals(expected, m);
+
+ // javadoc example
+ BlockRealMatrix matrix = new BlockRealMatrix
+ (new double[][] {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 0, 1 , 2}});
+ matrix.setSubMatrix(new double[][] {{3, 4}, {5, 6}}, 1, 1);
+ expected = new BlockRealMatrix
+ (new double[][] {{1, 2, 3, 4}, {5, 3, 4, 8}, {9, 5 ,6, 2}});
+ assertEquals(expected, matrix);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData,1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData,-1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData,1,-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new double[][] {{1}, {2, 3}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new double[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testWalk() throws MatrixVisitorException {
+ int rows = 150;
+ int columns = 75;
+
+ RealMatrix m = new BlockRealMatrix(rows, columns);
+ m.walkInRowOrder(new SetVisitor());
+ GetVisitor getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInRowOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInColumnOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInColumnOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new BlockRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ }
+
+ public void testSerial() {
+ BlockRealMatrix m = new BlockRealMatrix(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+ private static class SetVisitor extends DefaultRealMatrixChangingVisitor {
+ @Override
+ public double visit(int i, int j, double value) {
+ return i + j / 1024.0;
+ }
+ }
+
+ private static class GetVisitor extends DefaultRealMatrixPreservingVisitor {
+ private int count = 0;
+ @Override
+ public void visit(int i, int j, double value) {
+ ++count;
+ assertEquals(i + j / 1024.0, value, 0.0);
+ }
+ public int getCount() {
+ return count;
+ }
+ }
+
+ //--------------- -----------------Protected methods
+
+ /** verifies that two matrices are close (1-norm) */
+ protected void assertClose(RealMatrix m, RealMatrix n, double tolerance) {
+ assertTrue(m.subtract(n).getNorm() < tolerance);
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(double[] m, double[] n, double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors not same length");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(m[i], n[i], tolerance);
+ }
+ }
+
+ private BlockRealMatrix createRandomMatrix(Random r, int rows, int columns) {
+ BlockRealMatrix m = new BlockRealMatrix(rows, columns);
+ for (int i = 0; i < rows; ++i) {
+ for (int j = 0; j < columns; ++j) {
+ m.setEntry(i, j, 200 * r.nextDouble() - 100);
+ }
+ }
+ return m;
+ }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/CholeskyDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/CholeskyDecompositionImplTest.java
new file mode 100644
index 0000000..1b76eea
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/CholeskyDecompositionImplTest.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.linear;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.linear.CholeskyDecomposition;
+import org.apache.commons.math.linear.CholeskyDecompositionImpl;
+import org.apache.commons.math.linear.MatrixUtils;
+import org.apache.commons.math.linear.NonSquareMatrixException;
+import org.apache.commons.math.linear.NotPositiveDefiniteMatrixException;
+import org.apache.commons.math.linear.NotSymmetricMatrixException;
+import org.apache.commons.math.linear.RealMatrix;
+import org.junit.Test;
+
+public class CholeskyDecompositionImplTest {
+
+ private double[][] testData = new double[][] {
+ { 1, 2, 4, 7, 11 },
+ { 2, 13, 23, 38, 58 },
+ { 4, 23, 77, 122, 182 },
+ { 7, 38, 122, 294, 430 },
+ { 11, 58, 182, 430, 855 }
+ };
+
+ /** test dimensions */
+ @Test
+ public void testDimensions() throws MathException {
+ CholeskyDecomposition llt =
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(testData));
+ assertEquals(testData.length, llt.getL().getRowDimension());
+ assertEquals(testData.length, llt.getL().getColumnDimension());
+ assertEquals(testData.length, llt.getLT().getRowDimension());
+ assertEquals(testData.length, llt.getLT().getColumnDimension());
+ }
+
+ /** test non-square matrix */
+ @Test(expected = NonSquareMatrixException.class)
+ public void testNonSquare() throws MathException {
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(new double[3][2]));
+ }
+
+ /** test non-symmetric matrix */
+ @Test(expected = NotSymmetricMatrixException.class)
+ public void testNotSymmetricMatrixException() throws MathException {
+ double[][] changed = testData.clone();
+ changed[0][changed[0].length - 1] += 1.0e-5;
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(changed));
+ }
+
+ /** test non positive definite matrix */
+ @Test(expected = NotPositiveDefiniteMatrixException.class)
+ public void testNotPositiveDefinite() throws MathException {
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(new double[][] {
+ { 14, 11, 13, 15, 24 },
+ { 11, 34, 13, 8, 25 },
+ { 13, 13, 14, 15, 21 },
+ { 15, 8, 15, 18, 23 },
+ { 24, 25, 21, 23, 45 }
+ }));
+ }
+
+ @Test(expected = NotPositiveDefiniteMatrixException.class)
+ public void testMath274() throws MathException {
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(new double[][] {
+ { 0.40434286, -0.09376327, 0.30328980, 0.04909388 },
+ {-0.09376327, 0.10400408, 0.07137959, 0.04762857 },
+ { 0.30328980, 0.07137959, 0.30458776, 0.04882449 },
+ { 0.04909388, 0.04762857, 0.04882449, 0.07543265 }
+
+ }));
+ }
+
+ /** test A = LLT */
+ @Test
+ public void testAEqualLLT() throws MathException {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ CholeskyDecomposition llt = new CholeskyDecompositionImpl(matrix);
+ RealMatrix l = llt.getL();
+ RealMatrix lt = llt.getLT();
+ double norm = l.multiply(lt).subtract(matrix).getNorm();
+ assertEquals(0, norm, 1.0e-15);
+ }
+
+ /** test that L is lower triangular */
+ @Test
+ public void testLLowerTriangular() throws MathException {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ RealMatrix l = new CholeskyDecompositionImpl(matrix).getL();
+ for (int i = 0; i < l.getRowDimension(); i++) {
+ for (int j = i + 1; j < l.getColumnDimension(); j++) {
+ assertEquals(0.0, l.getEntry(i, j), 0.0);
+ }
+ }
+ }
+
+ /** test that LT is transpose of L */
+ @Test
+ public void testLTTransposed() throws MathException {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ CholeskyDecomposition llt = new CholeskyDecompositionImpl(matrix);
+ RealMatrix l = llt.getL();
+ RealMatrix lt = llt.getLT();
+ double norm = l.subtract(lt.transpose()).getNorm();
+ assertEquals(0, norm, 1.0e-15);
+ }
+
+ /** test matrices values */
+ @Test
+ public void testMatricesValues() throws MathException {
+ RealMatrix lRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 0, 0, 0, 0 },
+ { 2, 3, 0, 0, 0 },
+ { 4, 5, 6, 0, 0 },
+ { 7, 8, 9, 10, 0 },
+ { 11, 12, 13, 14, 15 }
+ });
+ CholeskyDecomposition llt =
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(testData));
+
+ // check values against known references
+ RealMatrix l = llt.getL();
+ assertEquals(0, l.subtract(lRef).getNorm(), 1.0e-13);
+ RealMatrix lt = llt.getLT();
+ assertEquals(0, lt.subtract(lRef.transpose()).getNorm(), 1.0e-13);
+
+ // check the same cached instance is returned the second time
+ assertTrue(l == llt.getL());
+ assertTrue(lt == llt.getLT());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/CholeskySolverTest.java b/src/test/java/org/apache/commons/math/linear/CholeskySolverTest.java
new file mode 100644
index 0000000..3b7e994
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/CholeskySolverTest.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.linear;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.MathException;
+
+public class CholeskySolverTest extends TestCase {
+
+ private double[][] testData = new double[][] {
+ { 1, 2, 4, 7, 11 },
+ { 2, 13, 23, 38, 58 },
+ { 4, 23, 77, 122, 182 },
+ { 7, 38, 122, 294, 430 },
+ { 11, 58, 182, 430, 855 }
+ };
+
+ public CholeskySolverTest(String name) {
+ super(name);
+ }
+
+ /** test solve dimension errors */
+ public void testSolveDimensionErrors() throws MathException {
+ DecompositionSolver solver =
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
+ try {
+ solver.solve(b);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test solve */
+ public void testSolve() throws MathException {
+ DecompositionSolver solver =
+ new CholeskyDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { 78, -13, 1 },
+ { 414, -62, -1 },
+ { 1312, -202, -37 },
+ { 2989, -542, 145 },
+ { 5510, -1465, 201 }
+ });
+ RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 0, 1 },
+ { 0, 1, 1 },
+ { 2, 1, -4 },
+ { 2, 2, 2 },
+ { 5, -3, 0 }
+ });
+
+ // using RealMatrix
+ assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), 1.0e-13);
+
+ // using double[]
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ new ArrayRealVector(solver.solve(b.getColumn(i))).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using ArrayRealVector
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using RealVector with an alternate implementation
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ ArrayRealVectorTest.RealVectorTestImpl v =
+ new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
+ assertEquals(0,
+ solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ }
+
+ /** test determinant */
+ public void testDeterminant() throws MathException {
+ assertEquals(7290000.0, getDeterminant(MatrixUtils.createRealMatrix(testData)), 1.0e-15);
+ }
+
+ private double getDeterminant(RealMatrix m) throws MathException {
+ return new CholeskyDecompositionImpl(m).getDeterminant();
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/EigenDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/EigenDecompositionImplTest.java
new file mode 100644
index 0000000..6a4bf75
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/EigenDecompositionImplTest.java
@@ -0,0 +1,533 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+
+public class EigenDecompositionImplTest extends TestCase {
+
+ private double[] refValues;
+ private RealMatrix matrix;
+
+ public EigenDecompositionImplTest(String name) {
+ super(name);
+ }
+
+ public void testDimension1() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] { { 1.5 } });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(1.5, ed.getRealEigenvalue(0), 1.0e-15);
+ }
+
+ public void testDimension2() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 59.0, 12.0 },
+ { 12.0, 66.0 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(75.0, ed.getRealEigenvalue(0), 1.0e-15);
+ assertEquals(50.0, ed.getRealEigenvalue(1), 1.0e-15);
+ }
+
+ public void testDimension3() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 39632.0, -4824.0, -16560.0 },
+ { -4824.0, 8693.0, 7920.0 },
+ { -16560.0, 7920.0, 17300.0 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(50000.0, ed.getRealEigenvalue(0), 3.0e-11);
+ assertEquals(12500.0, ed.getRealEigenvalue(1), 3.0e-11);
+ assertEquals( 3125.0, ed.getRealEigenvalue(2), 3.0e-11);
+ }
+
+ public void testDimension3MultipleRoot() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 5, 10, 15 },
+ { 10, 20, 30 },
+ { 15, 30, 45 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(70.0, ed.getRealEigenvalue(0), 3.0e-11);
+ assertEquals(0.0, ed.getRealEigenvalue(1), 3.0e-11);
+ assertEquals(0.0, ed.getRealEigenvalue(2), 3.0e-11);
+ }
+
+ public void testDimension4WithSplit() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 0.784, -0.288, 0.000, 0.000 },
+ { -0.288, 0.616, 0.000, 0.000 },
+ { 0.000, 0.000, 0.164, -0.048 },
+ { 0.000, 0.000, -0.048, 0.136 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(1.0, ed.getRealEigenvalue(0), 1.0e-15);
+ assertEquals(0.4, ed.getRealEigenvalue(1), 1.0e-15);
+ assertEquals(0.2, ed.getRealEigenvalue(2), 1.0e-15);
+ assertEquals(0.1, ed.getRealEigenvalue(3), 1.0e-15);
+ }
+
+ public void testDimension4WithoutSplit() {
+ RealMatrix matrix =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 0.5608, -0.2016, 0.1152, -0.2976 },
+ { -0.2016, 0.4432, -0.2304, 0.1152 },
+ { 0.1152, -0.2304, 0.3088, -0.1344 },
+ { -0.2976, 0.1152, -0.1344, 0.3872 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(1.0, ed.getRealEigenvalue(0), 1.0e-15);
+ assertEquals(0.4, ed.getRealEigenvalue(1), 1.0e-15);
+ assertEquals(0.2, ed.getRealEigenvalue(2), 1.0e-15);
+ assertEquals(0.1, ed.getRealEigenvalue(3), 1.0e-15);
+ }
+
+ // the following test triggered an ArrayIndexOutOfBoundsException in commons-math 2.0
+ public void testMath308() {
+
+ double[] mainTridiagonal = {
+ 22.330154644539597, 46.65485522478641, 17.393672330044705, 54.46687435351116, 80.17800767709437
+ };
+ double[] secondaryTridiagonal = {
+ 13.04450406501361, -5.977590941539671, 2.9040909856707517, 7.1570352792841225
+ };
+
+ // the reference values have been computed using routine DSTEMR
+ // from the fortran library LAPACK version 3.2.1
+ double[] refEigenValues = {
+ 82.044413207204002, 53.456697699894512, 52.536278520113882, 18.847969733754262, 14.138204224043099
+ };
+ RealVector[] refEigenVectors = {
+ new ArrayRealVector(new double[] { -0.000462690386766, -0.002118073109055, 0.011530080757413, 0.252322434584915, 0.967572088232592 }),
+ new ArrayRealVector(new double[] { 0.314647769490148, 0.750806415553905, -0.167700312025760, -0.537092972407375, 0.143854968127780 }),
+ new ArrayRealVector(new double[] { 0.222368839324646, 0.514921891363332, -0.021377019336614, 0.801196801016305, -0.207446991247740 }),
+ new ArrayRealVector(new double[] { -0.713933751051495, 0.190582113553930, -0.671410443368332, 0.056056055955050, -0.006541576993581 }),
+ new ArrayRealVector(new double[] { -0.584677060845929, 0.367177264979103, 0.721453187784497, -0.052971054621812, 0.005740715188257 })
+ };
+
+ EigenDecomposition decomposition =
+ new EigenDecompositionImpl(mainTridiagonal, secondaryTridiagonal, MathUtils.SAFE_MIN);
+
+ double[] eigenValues = decomposition.getRealEigenvalues();
+ for (int i = 0; i < refEigenValues.length; ++i) {
+ assertEquals(refEigenValues[i], eigenValues[i], 1.0e-5);
+ assertEquals(0, refEigenVectors[i].subtract(decomposition.getEigenvector(i)).getNorm(), 2.0e-7);
+ }
+
+ }
+
+ public void testMathpbx02() {
+
+ double[] mainTridiagonal = {
+ 7484.860960227216, 18405.28129035345, 13855.225609560746,
+ 10016.708722343366, 559.8117399576674, 6750.190788301587,
+ 71.21428769782159
+ };
+ double[] secondaryTridiagonal = {
+ -4175.088570476366,1975.7955858241994,5193.178422374075,
+ 1995.286659169179,75.34535882933804,-234.0808002076056
+ };
+
+ // the reference values have been computed using routine DSTEMR
+ // from the fortran library LAPACK version 3.2.1
+ double[] refEigenValues = {
+ 20654.744890306974412,16828.208208485466457,
+ 6893.155912634994820,6757.083016675340332,
+ 5887.799885688558788,64.309089923240379,
+ 57.992628792736340
+ };
+ RealVector[] refEigenVectors = {
+ new ArrayRealVector(new double[] {-0.270356342026904, 0.852811091326997, 0.399639490702077, 0.198794657813990, 0.019739323307666, 0.000106983022327, -0.000001216636321}),
+ new ArrayRealVector(new double[] {0.179995273578326,-0.402807848153042,0.701870993525734,0.555058211014888,0.068079148898236,0.000509139115227,-0.000007112235617}),
+ new ArrayRealVector(new double[] {-0.399582721284727,-0.056629954519333,-0.514406488522827,0.711168164518580,0.225548081276367,0.125943999652923,-0.004321507456014}),
+ new ArrayRealVector(new double[] {0.058515721572821,0.010200130057739,0.063516274916536,-0.090696087449378,-0.017148420432597,0.991318870265707,-0.034707338554096}),
+ new ArrayRealVector(new double[] {0.855205995537564,0.327134656629775,-0.265382397060548,0.282690729026706,0.105736068025572,-0.009138126622039,0.000367751821196}),
+ new ArrayRealVector(new double[] {-0.002913069901144,-0.005177515777101,0.041906334478672,-0.109315918416258,0.436192305456741,0.026307315639535,0.891797507436344}),
+ new ArrayRealVector(new double[] {-0.005738311176435,-0.010207611670378,0.082662420517928,-0.215733886094368,0.861606487840411,-0.025478530652759,-0.451080697503958})
+ };
+
+ // the following line triggers the exception
+ EigenDecomposition decomposition =
+ new EigenDecompositionImpl(mainTridiagonal, secondaryTridiagonal, MathUtils.SAFE_MIN);
+
+ double[] eigenValues = decomposition.getRealEigenvalues();
+ for (int i = 0; i < refEigenValues.length; ++i) {
+ assertEquals(refEigenValues[i], eigenValues[i], 1.0e-3);
+ if (refEigenVectors[i].dotProduct(decomposition.getEigenvector(i)) < 0) {
+ assertEquals(0, refEigenVectors[i].add(decomposition.getEigenvector(i)).getNorm(), 1.0e-5);
+ } else {
+ assertEquals(0, refEigenVectors[i].subtract(decomposition.getEigenvector(i)).getNorm(), 1.0e-5);
+ }
+ }
+
+ }
+
+ public void testMathpbx03() {
+
+ double[] mainTridiagonal = {
+ 1809.0978259647177,3395.4763425956166,1832.1894584712693,3804.364873592377,
+ 806.0482458637571,2403.656427234185,28.48691431556015
+ };
+ double[] secondaryTridiagonal = {
+ -656.8932064545833,-469.30804108920734,-1021.7714889369421,
+ -1152.540497328983,-939.9765163817368,-12.885877015422391
+ };
+
+ // the reference values have been computed using routine DSTEMR
+ // from the fortran library LAPACK version 3.2.1
+ double[] refEigenValues = {
+ 4603.121913685183245,3691.195818048970978,2743.442955402465032,1657.596442107321764,
+ 1336.797819095331306,30.129865209677519,17.035352085224986
+ };
+
+ RealVector[] refEigenVectors = {
+ new ArrayRealVector(new double[] {-0.036249830202337,0.154184732411519,-0.346016328392363,0.867540105133093,-0.294483395433451,0.125854235969548,-0.000354507444044}),
+ new ArrayRealVector(new double[] {-0.318654191697157,0.912992309960507,-0.129270874079777,-0.184150038178035,0.096521712579439,-0.070468788536461,0.000247918177736}),
+ new ArrayRealVector(new double[] {-0.051394668681147,0.073102235876933,0.173502042943743,-0.188311980310942,-0.327158794289386,0.905206581432676,-0.004296342252659}),
+ new ArrayRealVector(new double[] {0.838150199198361,0.193305209055716,-0.457341242126146,-0.166933875895419,0.094512811358535,0.119062381338757,-0.000941755685226}),
+ new ArrayRealVector(new double[] {0.438071395458547,0.314969169786246,0.768480630802146,0.227919171600705,-0.193317045298647,-0.170305467485594,0.001677380536009}),
+ new ArrayRealVector(new double[] {-0.003726503878741,-0.010091946369146,-0.067152015137611,-0.113798146542187,-0.313123000097908,-0.118940107954918,0.932862311396062}),
+ new ArrayRealVector(new double[] {0.009373003194332,0.025570377559400,0.170955836081348,0.291954519805750,0.807824267665706,0.320108347088646,0.360202112392266}),
+ };
+
+ // the following line triggers the exception
+ EigenDecomposition decomposition =
+ new EigenDecompositionImpl(mainTridiagonal, secondaryTridiagonal, MathUtils.SAFE_MIN);
+
+ double[] eigenValues = decomposition.getRealEigenvalues();
+ for (int i = 0; i < refEigenValues.length; ++i) {
+ assertEquals(refEigenValues[i], eigenValues[i], 1.0e-4);
+ if (refEigenVectors[i].dotProduct(decomposition.getEigenvector(i)) < 0) {
+ assertEquals(0, refEigenVectors[i].add(decomposition.getEigenvector(i)).getNorm(), 1.0e-5);
+ } else {
+ assertEquals(0, refEigenVectors[i].subtract(decomposition.getEigenvector(i)).getNorm(), 1.0e-5);
+ }
+ }
+
+ }
+
+ /** test a matrix already in tridiagonal form. */
+ public void testTridiagonal() {
+ Random r = new Random(4366663527842l);
+ double[] ref = new double[30];
+ for (int i = 0; i < ref.length; ++i) {
+ if (i < 5) {
+ ref[i] = 2 * r.nextDouble() - 1;
+ } else {
+ ref[i] = 0.0001 * r.nextDouble() + 6;
+ }
+ }
+ Arrays.sort(ref);
+ TriDiagonalTransformer t =
+ new TriDiagonalTransformer(createTestMatrix(r, ref));
+ EigenDecomposition ed =
+ new EigenDecompositionImpl(t.getMainDiagonalRef(),
+ t.getSecondaryDiagonalRef(),
+ MathUtils.SAFE_MIN);
+ double[] eigenValues = ed.getRealEigenvalues();
+ assertEquals(ref.length, eigenValues.length);
+ for (int i = 0; i < ref.length; ++i) {
+ assertEquals(ref[ref.length - i - 1], eigenValues[i], 2.0e-14);
+ }
+
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ final int m = matrix.getRowDimension();
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ assertEquals(m, ed.getV().getRowDimension());
+ assertEquals(m, ed.getV().getColumnDimension());
+ assertEquals(m, ed.getD().getColumnDimension());
+ assertEquals(m, ed.getD().getColumnDimension());
+ assertEquals(m, ed.getVT().getRowDimension());
+ assertEquals(m, ed.getVT().getColumnDimension());
+ }
+
+ /** test eigenvalues */
+ public void testEigenvalues() {
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ double[] eigenValues = ed.getRealEigenvalues();
+ assertEquals(refValues.length, eigenValues.length);
+ for (int i = 0; i < refValues.length; ++i) {
+ assertEquals(refValues[i], eigenValues[i], 3.0e-15);
+ }
+ }
+
+ /** test eigenvalues for a big matrix. */
+ public void testBigMatrix() {
+ Random r = new Random(17748333525117l);
+ double[] bigValues = new double[200];
+ for (int i = 0; i < bigValues.length; ++i) {
+ bigValues[i] = 2 * r.nextDouble() - 1;
+ }
+ Arrays.sort(bigValues);
+ EigenDecomposition ed =
+ new EigenDecompositionImpl(createTestMatrix(r, bigValues), MathUtils.SAFE_MIN);
+ double[] eigenValues = ed.getRealEigenvalues();
+ assertEquals(bigValues.length, eigenValues.length);
+ for (int i = 0; i < bigValues.length; ++i) {
+ assertEquals(bigValues[bigValues.length - i - 1], eigenValues[i], 2.0e-14);
+ }
+ }
+
+ /** test eigenvectors */
+ public void testEigenvectors() {
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ for (int i = 0; i < matrix.getRowDimension(); ++i) {
+ double lambda = ed.getRealEigenvalue(i);
+ RealVector v = ed.getEigenvector(i);
+ RealVector mV = matrix.operate(v);
+ assertEquals(0, mV.subtract(v.mapMultiplyToSelf(lambda)).getNorm(), 1.0e-13);
+ }
+ }
+
+ /** test A = VDVt */
+ public void testAEqualVDVt() {
+ EigenDecomposition ed = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN);
+ RealMatrix v = ed.getV();
+ RealMatrix d = ed.getD();
+ RealMatrix vT = ed.getVT();
+ double norm = v.multiply(d).multiply(vT).subtract(matrix).getNorm();
+ assertEquals(0, norm, 6.0e-13);
+ }
+
+ /** test that V is orthogonal */
+ public void testVOrthogonal() {
+ RealMatrix v = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN).getV();
+ RealMatrix vTv = v.transpose().multiply(v);
+ RealMatrix id = MatrixUtils.createRealIdentityMatrix(vTv.getRowDimension());
+ assertEquals(0, vTv.subtract(id).getNorm(), 2.0e-13);
+ }
+
+ /** test diagonal matrix */
+ public void testDiagonal() {
+ double[] diagonal = new double[] { -3.0, -2.0, 2.0, 5.0 };
+ RealMatrix m = createDiagonalMatrix(diagonal, diagonal.length, diagonal.length);
+ EigenDecomposition ed = new EigenDecompositionImpl(m, MathUtils.SAFE_MIN);
+ assertEquals(diagonal[0], ed.getRealEigenvalue(3), 2.0e-15);
+ assertEquals(diagonal[1], ed.getRealEigenvalue(2), 2.0e-15);
+ assertEquals(diagonal[2], ed.getRealEigenvalue(1), 2.0e-15);
+ assertEquals(diagonal[3], ed.getRealEigenvalue(0), 2.0e-15);
+ }
+
+ /**
+ * Matrix with eigenvalues {8, -1, -1}
+ */
+ public void testRepeatedEigenvalue() {
+ RealMatrix repeated = MatrixUtils.createRealMatrix(new double[][] {
+ {3, 2, 4},
+ {2, 0, 2},
+ {4, 2, 3}
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(repeated, MathUtils.SAFE_MIN);
+ checkEigenValues((new double[] {8, -1, -1}), ed, 1E-12);
+ checkEigenVector((new double[] {2, 1, 2}), ed, 1E-12);
+ }
+
+ /**
+ * Matrix with eigenvalues {2, 0, 12}
+ */
+ public void testDistinctEigenvalues() {
+ RealMatrix distinct = MatrixUtils.createRealMatrix(new double[][] {
+ {3, 1, -4},
+ {1, 3, -4},
+ {-4, -4, 8}
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(distinct, MathUtils.SAFE_MIN);
+ checkEigenValues((new double[] {2, 0, 12}), ed, 1E-12);
+ checkEigenVector((new double[] {1, -1, 0}), ed, 1E-12);
+ checkEigenVector((new double[] {1, 1, 1}), ed, 1E-12);
+ checkEigenVector((new double[] {-1, -1, 2}), ed, 1E-12);
+ }
+
+ /**
+ * Verifies operation on indefinite matrix
+ */
+ public void testZeroDivide() {
+ RealMatrix indefinite = MatrixUtils.createRealMatrix(new double [][] {
+ { 0.0, 1.0, -1.0 },
+ { 1.0, 1.0, 0.0 },
+ { -1.0,0.0, 1.0 }
+ });
+ EigenDecomposition ed = new EigenDecompositionImpl(indefinite, MathUtils.SAFE_MIN);
+ checkEigenValues((new double[] {2, 1, -1}), ed, 1E-12);
+ double isqrt3 = 1/FastMath.sqrt(3.0);
+ checkEigenVector((new double[] {isqrt3,isqrt3,-isqrt3}), ed, 1E-12);
+ double isqrt2 = 1/FastMath.sqrt(2.0);
+ checkEigenVector((new double[] {0.0,-isqrt2,-isqrt2}), ed, 1E-12);
+ double isqrt6 = 1/FastMath.sqrt(6.0);
+ checkEigenVector((new double[] {2*isqrt6,-isqrt6,isqrt6}), ed, 1E-12);
+ }
+ /**
+ * Verifies that the given EigenDecomposition has eigenvalues equivalent to
+ * the targetValues, ignoring the order of the values and allowing
+ * values to differ by tolerance.
+ */
+ protected void checkEigenValues(double[] targetValues,
+ EigenDecomposition ed, double tolerance) {
+ double[] observed = ed.getRealEigenvalues();
+ for (int i = 0; i < observed.length; i++) {
+ assertTrue(isIncludedValue(observed[i], targetValues, tolerance));
+ assertTrue(isIncludedValue(targetValues[i], observed, tolerance));
+ }
+ }
+
+
+ /**
+ * Returns true iff there is an entry within tolerance of value in
+ * searchArray.
+ */
+ private boolean isIncludedValue(double value, double[] searchArray,
+ double tolerance) {
+ boolean found = false;
+ int i = 0;
+ while (!found && i < searchArray.length) {
+ if (FastMath.abs(value - searchArray[i]) < tolerance) {
+ found = true;
+ }
+ i++;
+ }
+ return found;
+ }
+
+ /**
+ * Returns true iff eigenVector is a scalar multiple of one of the columns
+ * of ed.getV(). Does not try linear combinations - i.e., should only be
+ * used to find vectors in one-dimensional eigenspaces.
+ */
+ protected void checkEigenVector(double[] eigenVector,
+ EigenDecomposition ed, double tolerance) {
+ assertTrue(isIncludedColumn(eigenVector, ed.getV(), tolerance));
+ }
+
+ /**
+ * Returns true iff there is a column that is a scalar multiple of column
+ * in searchMatrix (modulo tolerance)
+ */
+ private boolean isIncludedColumn(double[] column, RealMatrix searchMatrix,
+ double tolerance) {
+ boolean found = false;
+ int i = 0;
+ while (!found && i < searchMatrix.getColumnDimension()) {
+ double multiplier = 1.0;
+ boolean matching = true;
+ int j = 0;
+ while (matching && j < searchMatrix.getRowDimension()) {
+ double colEntry = searchMatrix.getEntry(j, i);
+ // Use the first entry where both are non-zero as scalar
+ if (FastMath.abs(multiplier - 1.0) <= FastMath.ulp(1.0) && FastMath.abs(colEntry) > 1E-14
+ && FastMath.abs(column[j]) > 1e-14) {
+ multiplier = colEntry / column[j];
+ }
+ if (FastMath.abs(column[j] * multiplier - colEntry) > tolerance) {
+ matching = false;
+ }
+ j++;
+ }
+ found = matching;
+ i++;
+ }
+ return found;
+ }
+
+ @Override
+ public void setUp() {
+ refValues = new double[] {
+ 2.003, 2.002, 2.001, 1.001, 1.000, 0.001
+ };
+ matrix = createTestMatrix(new Random(35992629946426l), refValues);
+ }
+
+ @Override
+ public void tearDown() {
+ refValues = null;
+ matrix = null;
+ }
+
+ static RealMatrix createTestMatrix(final Random r, final double[] eigenValues) {
+ final int n = eigenValues.length;
+ final RealMatrix v = createOrthogonalMatrix(r, n);
+ final RealMatrix d = createDiagonalMatrix(eigenValues, n, n);
+ return v.multiply(d).multiply(v.transpose());
+ }
+
+ public static RealMatrix createOrthogonalMatrix(final Random r, final int size) {
+
+ final double[][] data = new double[size][size];
+
+ for (int i = 0; i < size; ++i) {
+ final double[] dataI = data[i];
+ double norm2 = 0;
+ do {
+
+ // generate randomly row I
+ for (int j = 0; j < size; ++j) {
+ dataI[j] = 2 * r.nextDouble() - 1;
+ }
+
+ // project the row in the subspace orthogonal to previous rows
+ for (int k = 0; k < i; ++k) {
+ final double[] dataK = data[k];
+ double dotProduct = 0;
+ for (int j = 0; j < size; ++j) {
+ dotProduct += dataI[j] * dataK[j];
+ }
+ for (int j = 0; j < size; ++j) {
+ dataI[j] -= dotProduct * dataK[j];
+ }
+ }
+
+ // normalize the row
+ norm2 = 0;
+ for (final double dataIJ : dataI) {
+ norm2 += dataIJ * dataIJ;
+ }
+ final double inv = 1.0 / FastMath.sqrt(norm2);
+ for (int j = 0; j < size; ++j) {
+ dataI[j] *= inv;
+ }
+
+ } while (norm2 * size < 0.01);
+ }
+
+ return MatrixUtils.createRealMatrix(data);
+
+ }
+
+ public static RealMatrix createDiagonalMatrix(final double[] diagonal,
+ final int rows, final int columns) {
+ final double[][] dData = new double[rows][columns];
+ for (int i = 0; i < FastMath.min(rows, columns); ++i) {
+ dData[i][i] = diagonal[i];
+ }
+ return MatrixUtils.createRealMatrix(dData);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/EigenSolverTest.java b/src/test/java/org/apache/commons/math/linear/EigenSolverTest.java
new file mode 100644
index 0000000..18e7c53
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/EigenSolverTest.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.linear;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.util.MathUtils;
+
+public class EigenSolverTest extends TestCase {
+
+ private double[] refValues;
+ private RealMatrix matrix;
+
+ public EigenSolverTest(String name) {
+ super(name);
+ }
+
+ /** test non invertible matrix */
+ public void testNonInvertible() {
+ Random r = new Random(9994100315209l);
+ RealMatrix m =
+ EigenDecompositionImplTest.createTestMatrix(r, new double[] { 1.0, 0.0, -1.0, -2.0, -3.0 });
+ DecompositionSolver es = new EigenDecompositionImpl(m, MathUtils.SAFE_MIN).getSolver();
+ assertFalse(es.isNonSingular());
+ try {
+ es.getInverse();
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ }
+
+ /** test invertible matrix */
+ public void testInvertible() {
+ Random r = new Random(9994100315209l);
+ RealMatrix m =
+ EigenDecompositionImplTest.createTestMatrix(r, new double[] { 1.0, 0.5, -1.0, -2.0, -3.0 });
+ DecompositionSolver es = new EigenDecompositionImpl(m, MathUtils.SAFE_MIN).getSolver();
+ assertTrue(es.isNonSingular());
+ RealMatrix inverse = es.getInverse();
+ RealMatrix error =
+ m.multiply(inverse).subtract(MatrixUtils.createRealIdentityMatrix(m.getRowDimension()));
+ assertEquals(0, error.getNorm(), 4.0e-15);
+ }
+
+ /** test solve dimension errors */
+ public void testSolveDimensionErrors() {
+ DecompositionSolver es = new EigenDecompositionImpl(matrix, MathUtils.SAFE_MIN).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
+ try {
+ es.solve(b);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ es.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ es.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test solve */
+ public void testSolve() {
+ RealMatrix m = MatrixUtils.createRealMatrix(new double[][] {
+ { 91, 5, 29, 32, 40, 14 },
+ { 5, 34, -1, 0, 2, -1 },
+ { 29, -1, 12, 9, 21, 8 },
+ { 32, 0, 9, 14, 9, 0 },
+ { 40, 2, 21, 9, 51, 19 },
+ { 14, -1, 8, 0, 19, 14 }
+ });
+ DecompositionSolver es = new EigenDecompositionImpl(m, MathUtils.SAFE_MIN).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { 1561, 269, 188 },
+ { 69, -21, 70 },
+ { 739, 108, 63 },
+ { 324, 86, 59 },
+ { 1624, 194, 107 },
+ { 796, 69, 36 }
+ });
+ RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 2, 1 },
+ { 2, -1, 2 },
+ { 4, 2, 3 },
+ { 8, -1, 0 },
+ { 16, 2, 0 },
+ { 32, -1, 0 }
+ });
+
+ // using RealMatrix
+ RealMatrix solution=es.solve(b);
+ assertEquals(0, solution.subtract(xRef).getNorm(), 2.5e-12);
+
+ // using double[]
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ new ArrayRealVector(es.solve(b.getColumn(i))).subtract(xRef.getColumnVector(i)).getNorm(),
+ 2.0e-11);
+ }
+
+ // using Array2DRowRealMatrix
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ es.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
+ 2.0e-11);
+ }
+
+ // using RealMatrix with an alternate implementation
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ ArrayRealVectorTest.RealVectorTestImpl v =
+ new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
+ assertEquals(0,
+ es.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
+ 2.0e-11);
+ }
+
+ }
+
+ @Override
+ public void setUp() {
+ refValues = new double[] {
+ 2.003, 2.002, 2.001, 1.001, 1.000, 0.001
+ };
+ matrix = EigenDecompositionImplTest.createTestMatrix(new Random(35992629946426l), refValues);
+ }
+
+ @Override
+ public void tearDown() {
+ refValues = null;
+ matrix = null;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/FieldLUDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/FieldLUDecompositionImplTest.java
new file mode 100644
index 0000000..30460b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/FieldLUDecompositionImplTest.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionField;
+
+public class FieldLUDecompositionImplTest extends TestCase {
+ private Fraction[][] testData = {
+ { new Fraction(1), new Fraction(2), new Fraction(3)},
+ { new Fraction(2), new Fraction(5), new Fraction(3)},
+ { new Fraction(1), new Fraction(0), new Fraction(8)}
+ };
+ private Fraction[][] testDataMinus = {
+ { new Fraction(-1), new Fraction(-2), new Fraction(-3)},
+ { new Fraction(-2), new Fraction(-5), new Fraction(-3)},
+ { new Fraction(-1), new Fraction(0), new Fraction(-8)}
+ };
+ private Fraction[][] luData = {
+ { new Fraction(2), new Fraction(3), new Fraction(3) },
+ { new Fraction(2), new Fraction(3), new Fraction(7) },
+ { new Fraction(6), new Fraction(6), new Fraction(8) }
+ };
+
+ // singular matrices
+ private Fraction[][] singular = {
+ { new Fraction(2), new Fraction(3) },
+ { new Fraction(2), new Fraction(3) }
+ };
+ private Fraction[][] bigSingular = {
+ { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) },
+ { new Fraction(2), new Fraction(5), new Fraction(3), new Fraction(4) },
+ { new Fraction(7), new Fraction(3), new Fraction(256), new Fraction(1930) },
+ { new Fraction(3), new Fraction(7), new Fraction(6), new Fraction(8) }
+ }; // 4th row = 1st + 2nd
+
+ public FieldLUDecompositionImplTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldLUDecomposition<Fraction> LU = new FieldLUDecompositionImpl<Fraction>(matrix);
+ assertEquals(testData.length, LU.getL().getRowDimension());
+ assertEquals(testData.length, LU.getL().getColumnDimension());
+ assertEquals(testData.length, LU.getU().getRowDimension());
+ assertEquals(testData.length, LU.getU().getColumnDimension());
+ assertEquals(testData.length, LU.getP().getRowDimension());
+ assertEquals(testData.length, LU.getP().getColumnDimension());
+
+ }
+
+ /** test non-square matrix */
+ public void testNonSquare() {
+ try {
+ new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { Fraction.ZERO, Fraction.ZERO },
+ { Fraction.ZERO, Fraction.ZERO },
+ { Fraction.ZERO, Fraction.ZERO }
+ }));
+ fail("Expected InvalidMatrixException");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ }
+
+ /** test PA = LU */
+ public void testPAEqualLU() {
+ FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldLUDecomposition<Fraction> lu = new FieldLUDecompositionImpl<Fraction>(matrix);
+ FieldMatrix<Fraction> l = lu.getL();
+ FieldMatrix<Fraction> u = lu.getU();
+ FieldMatrix<Fraction> p = lu.getP();
+ TestUtils.assertEquals(p.multiply(matrix), l.multiply(u));
+
+ matrix = new Array2DRowFieldMatrix<Fraction>(testDataMinus);
+ lu = new FieldLUDecompositionImpl<Fraction>(matrix);
+ l = lu.getL();
+ u = lu.getU();
+ p = lu.getP();
+ TestUtils.assertEquals(p.multiply(matrix), l.multiply(u));
+
+ matrix = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), 17, 17);
+ for (int i = 0; i < matrix.getRowDimension(); ++i) {
+ matrix.setEntry(i, i, Fraction.ONE);
+ }
+ lu = new FieldLUDecompositionImpl<Fraction>(matrix);
+ l = lu.getL();
+ u = lu.getU();
+ p = lu.getP();
+ TestUtils.assertEquals(p.multiply(matrix), l.multiply(u));
+
+ matrix = new Array2DRowFieldMatrix<Fraction>(singular);
+ lu = new FieldLUDecompositionImpl<Fraction>(matrix);
+ assertFalse(lu.getSolver().isNonSingular());
+ assertNull(lu.getL());
+ assertNull(lu.getU());
+ assertNull(lu.getP());
+
+ matrix = new Array2DRowFieldMatrix<Fraction>(bigSingular);
+ lu = new FieldLUDecompositionImpl<Fraction>(matrix);
+ assertFalse(lu.getSolver().isNonSingular());
+ assertNull(lu.getL());
+ assertNull(lu.getU());
+ assertNull(lu.getP());
+
+ }
+
+ /** test that L is lower triangular with unit diagonal */
+ public void testLLowerTriangular() {
+ FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldMatrix<Fraction> l = new FieldLUDecompositionImpl<Fraction>(matrix).getL();
+ for (int i = 0; i < l.getRowDimension(); i++) {
+ assertEquals(Fraction.ONE, l.getEntry(i, i));
+ for (int j = i + 1; j < l.getColumnDimension(); j++) {
+ assertEquals(Fraction.ZERO, l.getEntry(i, j));
+ }
+ }
+ }
+
+ /** test that U is upper triangular */
+ public void testUUpperTriangular() {
+ FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldMatrix<Fraction> u = new FieldLUDecompositionImpl<Fraction>(matrix).getU();
+ for (int i = 0; i < u.getRowDimension(); i++) {
+ for (int j = 0; j < i; j++) {
+ assertEquals(Fraction.ZERO, u.getEntry(i, j));
+ }
+ }
+ }
+
+ /** test that P is a permutation matrix */
+ public void testPPermutation() {
+ FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldMatrix<Fraction> p = new FieldLUDecompositionImpl<Fraction>(matrix).getP();
+
+ FieldMatrix<Fraction> ppT = p.multiply(p.transpose());
+ FieldMatrix<Fraction> id =
+ new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(),
+ p.getRowDimension(), p.getRowDimension());
+ for (int i = 0; i < id.getRowDimension(); ++i) {
+ id.setEntry(i, i, Fraction.ONE);
+ }
+ TestUtils.assertEquals(id, ppT);
+
+ for (int i = 0; i < p.getRowDimension(); i++) {
+ int zeroCount = 0;
+ int oneCount = 0;
+ int otherCount = 0;
+ for (int j = 0; j < p.getColumnDimension(); j++) {
+ final Fraction e = p.getEntry(i, j);
+ if (e.equals(Fraction.ZERO)) {
+ ++zeroCount;
+ } else if (e.equals(Fraction.ONE)) {
+ ++oneCount;
+ } else {
+ ++otherCount;
+ }
+ }
+ assertEquals(p.getColumnDimension() - 1, zeroCount);
+ assertEquals(1, oneCount);
+ assertEquals(0, otherCount);
+ }
+
+ for (int j = 0; j < p.getColumnDimension(); j++) {
+ int zeroCount = 0;
+ int oneCount = 0;
+ int otherCount = 0;
+ for (int i = 0; i < p.getRowDimension(); i++) {
+ final Fraction e = p.getEntry(i, j);
+ if (e.equals(Fraction.ZERO)) {
+ ++zeroCount;
+ } else if (e.equals(Fraction.ONE)) {
+ ++oneCount;
+ } else {
+ ++otherCount;
+ }
+ }
+ assertEquals(p.getRowDimension() - 1, zeroCount);
+ assertEquals(1, oneCount);
+ assertEquals(0, otherCount);
+ }
+
+ }
+
+
+ /** test singular */
+ public void testSingular() {
+ FieldLUDecomposition<Fraction> lu =
+ new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(testData));
+ assertTrue(lu.getSolver().isNonSingular());
+ lu = new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(singular));
+ assertFalse(lu.getSolver().isNonSingular());
+ lu = new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(bigSingular));
+ assertFalse(lu.getSolver().isNonSingular());
+ }
+
+ /** test matrices values */
+ public void testMatricesValues1() {
+ FieldLUDecomposition<Fraction> lu =
+ new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(testData));
+ FieldMatrix<Fraction> lRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(0), new Fraction(0) },
+ { new Fraction(2), new Fraction(1), new Fraction(0) },
+ { new Fraction(1), new Fraction(-2), new Fraction(1) }
+ });
+ FieldMatrix<Fraction> uRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(2), new Fraction(3) },
+ { new Fraction(0), new Fraction(1), new Fraction(-3) },
+ { new Fraction(0), new Fraction(0), new Fraction(-1) }
+ });
+ FieldMatrix<Fraction> pRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(0), new Fraction(0) },
+ { new Fraction(0), new Fraction(1), new Fraction(0) },
+ { new Fraction(0), new Fraction(0), new Fraction(1) }
+ });
+ int[] pivotRef = { 0, 1, 2 };
+
+ // check values against known references
+ FieldMatrix<Fraction> l = lu.getL();
+ TestUtils.assertEquals(lRef, l);
+ FieldMatrix<Fraction> u = lu.getU();
+ TestUtils.assertEquals(uRef, u);
+ FieldMatrix<Fraction> p = lu.getP();
+ TestUtils.assertEquals(pRef, p);
+ int[] pivot = lu.getPivot();
+ for (int i = 0; i < pivotRef.length; ++i) {
+ assertEquals(pivotRef[i], pivot[i]);
+ }
+
+ // check the same cached instance is returned the second time
+ assertTrue(l == lu.getL());
+ assertTrue(u == lu.getU());
+ assertTrue(p == lu.getP());
+
+ }
+
+ /** test matrices values */
+ public void testMatricesValues2() {
+ FieldLUDecomposition<Fraction> lu =
+ new FieldLUDecompositionImpl<Fraction>(new Array2DRowFieldMatrix<Fraction>(luData));
+ FieldMatrix<Fraction> lRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(0), new Fraction(0) },
+ { new Fraction(3), new Fraction(1), new Fraction(0) },
+ { new Fraction(1), new Fraction(0), new Fraction(1) }
+ });
+ FieldMatrix<Fraction> uRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(2), new Fraction(3), new Fraction(3) },
+ { new Fraction(0), new Fraction(-3), new Fraction(-1) },
+ { new Fraction(0), new Fraction(0), new Fraction(4) }
+ });
+ FieldMatrix<Fraction> pRef = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(0), new Fraction(0) },
+ { new Fraction(0), new Fraction(0), new Fraction(1) },
+ { new Fraction(0), new Fraction(1), new Fraction(0) }
+ });
+ int[] pivotRef = { 0, 2, 1 };
+
+ // check values against known references
+ FieldMatrix<Fraction> l = lu.getL();
+ TestUtils.assertEquals(lRef, l);
+ FieldMatrix<Fraction> u = lu.getU();
+ TestUtils.assertEquals(uRef, u);
+ FieldMatrix<Fraction> p = lu.getP();
+ TestUtils.assertEquals(pRef, p);
+ int[] pivot = lu.getPivot();
+ for (int i = 0; i < pivotRef.length; ++i) {
+ assertEquals(pivotRef[i], pivot[i]);
+ }
+
+ // check the same cached instance is returned the second time
+ assertTrue(l == lu.getL());
+ assertTrue(u == lu.getU());
+ assertTrue(p == lu.getP());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/FieldMatrixImplTest.java b/src/test/java/org/apache/commons/math/linear/FieldMatrixImplTest.java
new file mode 100644
index 0000000..f761c10
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/FieldMatrixImplTest.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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionField;
+
+/**
+ * Test cases for the {@link Array2DRowFieldMatrix} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+
+public final class FieldMatrixImplTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected Fraction[][] id = { {new Fraction(1),new Fraction(0),new Fraction(0)}, {new Fraction(0),new Fraction(1),new Fraction(0)}, {new Fraction(0),new Fraction(0),new Fraction(1)} };
+
+ // Test data for group operations
+ protected Fraction[][] testData = { {new Fraction(1),new Fraction(2),new Fraction(3)}, {new Fraction(2),new Fraction(5),new Fraction(3)}, {new Fraction(1),new Fraction(0),new Fraction(8)} };
+ protected Fraction[][] testDataLU = {{new Fraction(2), new Fraction(5), new Fraction(3)}, {new Fraction(1, 2), new Fraction(-5, 2), new Fraction(13, 2)}, {new Fraction(1, 2), new Fraction(1, 5), new Fraction(1, 5)}};
+ protected Fraction[][] testDataPlus2 = { {new Fraction(3),new Fraction(4),new Fraction(5)}, {new Fraction(4),new Fraction(7),new Fraction(5)}, {new Fraction(3),new Fraction(2),new Fraction(10)} };
+ protected Fraction[][] testDataMinus = { {new Fraction(-1),new Fraction(-2),new Fraction(-3)}, {new Fraction(-2),new Fraction(-5),new Fraction(-3)},
+ {new Fraction(-1),new Fraction(0),new Fraction(-8)} };
+ protected Fraction[] testDataRow1 = {new Fraction(1),new Fraction(2),new Fraction(3)};
+ protected Fraction[] testDataCol3 = {new Fraction(3),new Fraction(3),new Fraction(8)};
+ protected Fraction[][] testDataInv =
+ { {new Fraction(-40),new Fraction(16),new Fraction(9)}, {new Fraction(13),new Fraction(-5),new Fraction(-3)}, {new Fraction(5),new Fraction(-2),new Fraction(-1)} };
+ protected Fraction[] preMultTest = {new Fraction(8),new Fraction(12),new Fraction(33)};
+ protected Fraction[][] testData2 ={ {new Fraction(1),new Fraction(2),new Fraction(3)}, {new Fraction(2),new Fraction(5),new Fraction(3)}};
+ protected Fraction[][] testData2T = { {new Fraction(1),new Fraction(2)}, {new Fraction(2),new Fraction(5)}, {new Fraction(3),new Fraction(3)}};
+ protected Fraction[][] testDataPlusInv =
+ { {new Fraction(-39),new Fraction(18),new Fraction(12)}, {new Fraction(15),new Fraction(0),new Fraction(0)}, {new Fraction(6),new Fraction(-2),new Fraction(7)} };
+
+ // lu decomposition tests
+ protected Fraction[][] luData = { {new Fraction(2),new Fraction(3),new Fraction(3)}, {new Fraction(0),new Fraction(5),new Fraction(7)}, {new Fraction(6),new Fraction(9),new Fraction(8)} };
+ protected Fraction[][] luDataLUDecomposition = { {new Fraction(6),new Fraction(9),new Fraction(8)}, {new Fraction(0),new Fraction(5),new Fraction(7)},
+ {new Fraction(1, 3),new Fraction(0),new Fraction(1, 3)} };
+
+ // singular matrices
+ protected Fraction[][] singular = { {new Fraction(2),new Fraction(3)}, {new Fraction(2),new Fraction(3)} };
+ protected Fraction[][] bigSingular = {{new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)}, {new Fraction(2),new Fraction(5),new Fraction(3),new Fraction(4)},
+ {new Fraction(7),new Fraction(3),new Fraction(256),new Fraction(1930)}, {new Fraction(3),new Fraction(7),new Fraction(6),new Fraction(8)}}; // 4th row = 1st + 2nd
+ protected Fraction[][] detData = { {new Fraction(1),new Fraction(2),new Fraction(3)}, {new Fraction(4),new Fraction(5),new Fraction(6)}, {new Fraction(7),new Fraction(8),new Fraction(10)} };
+ protected Fraction[][] detData2 = { {new Fraction(1), new Fraction(3)}, {new Fraction(2), new Fraction(4)}};
+
+ // vectors
+ protected Fraction[] testVector = {new Fraction(1),new Fraction(2),new Fraction(3)};
+ protected Fraction[] testVector2 = {new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)};
+
+ // submatrix accessor tests
+ protected Fraction[][] subTestData = {{new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4)}, {new Fraction(3, 2), new Fraction(5, 2), new Fraction(7, 2), new Fraction(9, 2)},
+ {new Fraction(2), new Fraction(4), new Fraction(6), new Fraction(8)}, {new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7)}};
+ // array selections
+ protected Fraction[][] subRows02Cols13 = { {new Fraction(2), new Fraction(4)}, {new Fraction(4), new Fraction(8)}};
+ protected Fraction[][] subRows03Cols12 = { {new Fraction(2), new Fraction(3)}, {new Fraction(5), new Fraction(6)}};
+ protected Fraction[][] subRows03Cols123 = { {new Fraction(2), new Fraction(3), new Fraction(4)} , {new Fraction(5), new Fraction(6), new Fraction(7)}};
+ // effective permutations
+ protected Fraction[][] subRows20Cols123 = { {new Fraction(4), new Fraction(6), new Fraction(8)} , {new Fraction(2), new Fraction(3), new Fraction(4)}};
+ protected Fraction[][] subRows31Cols31 = {{new Fraction(7), new Fraction(5)}, {new Fraction(9, 2), new Fraction(5, 2)}};
+ // contiguous ranges
+ protected Fraction[][] subRows01Cols23 = {{new Fraction(3),new Fraction(4)} , {new Fraction(7, 2), new Fraction(9, 2)}};
+ protected Fraction[][] subRows23Cols00 = {{new Fraction(2)} , {new Fraction(4)}};
+ protected Fraction[][] subRows00Cols33 = {{new Fraction(4)}};
+ // row matrices
+ protected Fraction[][] subRow0 = {{new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)}};
+ protected Fraction[][] subRow3 = {{new Fraction(4),new Fraction(5),new Fraction(6),new Fraction(7)}};
+ // column matrices
+ protected Fraction[][] subColumn1 = {{new Fraction(2)}, {new Fraction(5, 2)}, {new Fraction(4)}, {new Fraction(5)}};
+ protected Fraction[][] subColumn3 = {{new Fraction(4)}, {new Fraction(9, 2)}, {new Fraction(8)}, {new Fraction(7)}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public FieldMatrixImplTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ Array2DRowFieldMatrix<Fraction> m1 = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(m1.getData());
+ assertEquals(m2,m1);
+ Array2DRowFieldMatrix<Fraction> m3 = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m4 = new Array2DRowFieldMatrix<Fraction>(m3.getData(), false);
+ assertEquals(m4,m3);
+ }
+
+ /** test add */
+ public void testAdd() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> mInv = new Array2DRowFieldMatrix<Fraction>(testDataInv);
+ FieldMatrix<Fraction> mPlusMInv = m.add(mInv);
+ Fraction[][] sumEntries = mPlusMInv.getData();
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals(testDataPlusInv[row][col],sumEntries[row][col]);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(testDataInv);
+ TestUtils.assertEquals(m.subtract(m2),m2.scalarMultiply(new Fraction(-1)).add(m));
+ try {
+ m.subtract(new Array2DRowFieldMatrix<Fraction>(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> mInv = new Array2DRowFieldMatrix<Fraction>(testDataInv);
+ Array2DRowFieldMatrix<Fraction> identity = new Array2DRowFieldMatrix<Fraction>(id);
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(testData2);
+ TestUtils.assertEquals(m.multiply(mInv), identity);
+ TestUtils.assertEquals(mInv.multiply(m), identity);
+ TestUtils.assertEquals(m.multiply(identity), m);
+ TestUtils.assertEquals(identity.multiply(mInv), mInv);
+ TestUtils.assertEquals(m2.multiply(identity), m2);
+ try {
+ m.multiply(new Array2DRowFieldMatrix<Fraction>(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ //Additional Test for Array2DRowFieldMatrix<Fraction>Test.testMultiply
+
+ private Fraction[][] d3 = new Fraction[][] {{new Fraction(1),new Fraction(2),new Fraction(3),new Fraction(4)},{new Fraction(5),new Fraction(6),new Fraction(7),new Fraction(8)}};
+ private Fraction[][] d4 = new Fraction[][] {{new Fraction(1)},{new Fraction(2)},{new Fraction(3)},{new Fraction(4)}};
+ private Fraction[][] d5 = new Fraction[][] {{new Fraction(30)},{new Fraction(70)}};
+
+ public void testMultiply2() {
+ FieldMatrix<Fraction> m3 = new Array2DRowFieldMatrix<Fraction>(d3);
+ FieldMatrix<Fraction> m4 = new Array2DRowFieldMatrix<Fraction>(d4);
+ FieldMatrix<Fraction> m5 = new Array2DRowFieldMatrix<Fraction>(d5);
+ TestUtils.assertEquals(m3.multiply(m4), m5);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(id);
+ assertEquals("identity trace",new Fraction(3),m.getTrace());
+ m = new Array2DRowFieldMatrix<Fraction>(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(new Array2DRowFieldMatrix<Fraction>(testDataPlus2), m.scalarAdd(new Fraction(2)));
+ }
+
+ /** test operate */
+ public void testOperate() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(id);
+ TestUtils.assertEquals(testVector, m.operate(testVector));
+ TestUtils.assertEquals(testVector, m.operate(new ArrayFieldVector<Fraction>(testVector)).getData());
+ m = new Array2DRowFieldMatrix<Fraction>(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ FieldMatrix<Fraction> a = new Array2DRowFieldMatrix<Fraction>(new Fraction[][] {
+ { new Fraction(1), new Fraction(2) }, { new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6) }
+ }, false);
+ Fraction[] b = a.operate(new Fraction[] { new Fraction(1), new Fraction(1) });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( new Fraction(3), b[0]);
+ assertEquals( new Fraction(7), b[1]);
+ assertEquals(new Fraction(11), b[2]);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ FieldMatrix<Fraction> mIT = new FieldLUDecompositionImpl<Fraction>(m).getSolver().getInverse().transpose();
+ FieldMatrix<Fraction> mTI = new FieldLUDecompositionImpl<Fraction>(m.transpose()).getSolver().getInverse();
+ TestUtils.assertEquals(mIT, mTI);
+ m = new Array2DRowFieldMatrix<Fraction>(testData2);
+ FieldMatrix<Fraction> mt = new Array2DRowFieldMatrix<Fraction>(testData2T);
+ TestUtils.assertEquals(mt, m.transpose());
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(m.preMultiply(testVector), preMultTest);
+ TestUtils.assertEquals(m.preMultiply(new ArrayFieldVector<Fraction>(testVector).getData()),
+ preMultTest);
+ m = new Array2DRowFieldMatrix<Fraction>(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ FieldMatrix<Fraction> m3 = new Array2DRowFieldMatrix<Fraction>(d3);
+ FieldMatrix<Fraction> m4 = new Array2DRowFieldMatrix<Fraction>(d4);
+ FieldMatrix<Fraction> m5 = new Array2DRowFieldMatrix<Fraction>(d5);
+ TestUtils.assertEquals(m4.preMultiply(m3), m5);
+
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> mInv = new Array2DRowFieldMatrix<Fraction>(testDataInv);
+ Array2DRowFieldMatrix<Fraction> identity = new Array2DRowFieldMatrix<Fraction>(id);
+ TestUtils.assertEquals(m.preMultiply(mInv), identity);
+ TestUtils.assertEquals(mInv.preMultiply(m), identity);
+ TestUtils.assertEquals(m.preMultiply(identity), m);
+ TestUtils.assertEquals(identity.preMultiply(mInv), mInv);
+ try {
+ m.preMultiply(new Array2DRowFieldMatrix<Fraction>(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ TestUtils.assertEquals(m.getRow(0), testDataRow1);
+ TestUtils.assertEquals(m.getColumn(2), testDataCol3);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ assertEquals("get entry",m.getEntry(0,1),new Fraction(2));
+ try {
+ m.getEntry(10, 4);
+ fail ("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ Fraction[][] matrixData = {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(2),new Fraction(5),new Fraction(3)}
+ };
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(matrixData);
+ // One more with three rows, two columns
+ Fraction[][] matrixData2 = {
+ {new Fraction(1),new Fraction(2)},
+ {new Fraction(2),new Fraction(5)},
+ {new Fraction(1), new Fraction(7)}
+ };
+ FieldMatrix<Fraction> n = new Array2DRowFieldMatrix<Fraction>(matrixData2);
+ // Now multiply m by n
+ FieldMatrix<Fraction> p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ FieldMatrix<Fraction> pInverse = new FieldLUDecompositionImpl<Fraction>(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ Fraction[][] coefficientsData = {
+ {new Fraction(2), new Fraction(3), new Fraction(-2)},
+ {new Fraction(-1), new Fraction(7), new Fraction(6)},
+ {new Fraction(4), new Fraction(-3), new Fraction(-5)}
+ };
+ FieldMatrix<Fraction> coefficients = new Array2DRowFieldMatrix<Fraction>(coefficientsData);
+ Fraction[] constants = {new Fraction(1), new Fraction(-2), new Fraction(1)};
+ Fraction[] solution = new FieldLUDecompositionImpl<Fraction>(coefficients).getSolver().solve(constants);
+ assertEquals(new Fraction(2).multiply(solution[0]).
+ add(new Fraction(3).multiply(solution[1])).
+ subtract(new Fraction(2).multiply(solution[2])), constants[0]);
+ assertEquals(new Fraction(-1).multiply(solution[0]).
+ add(new Fraction(7).multiply(solution[1])).
+ add(new Fraction(6).multiply(solution[2])), constants[1]);
+ assertEquals(new Fraction(4).multiply(solution[0]).
+ subtract(new Fraction(3).multiply(solution[1])).
+ subtract(new Fraction(5).multiply(solution[2])), constants[2]);
+
+ }
+
+ // test submatrix accessors
+ public void testGetSubMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ checkGetSubMatrix(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkGetSubMatrix(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkGetSubMatrix(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkGetSubMatrix(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkGetSubMatrix(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkGetSubMatrix(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, -1, 1, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 2);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4);
+ checkGetSubMatrix(m, null, new int[] {}, new int[] { 0 });
+ checkGetSubMatrix(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkGetSubMatrix(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ FieldMatrix<Fraction> sub = m.getSubMatrix(startRow, endRow, startColumn, endColumn);
+ if (reference != null) {
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkGetSubMatrix(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ FieldMatrix<Fraction> sub = m.getSubMatrix(selectedRows, selectedColumns);
+ if (reference != null) {
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(reference), sub);
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testCopySubMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ checkCopy(m, subRows23Cols00, 2 , 3 , 0, 0);
+ checkCopy(m, subRows00Cols33, 0 , 0 , 3, 3);
+ checkCopy(m, subRows01Cols23, 0 , 1 , 2, 3);
+ checkCopy(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 });
+ checkCopy(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 });
+ checkCopy(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 });
+
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, -1, 1, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 2);
+ checkCopy(m, null, 1, 0, 2, 4);
+ checkCopy(m, null, new int[] {}, new int[] { 0 });
+ checkCopy(m, null, new int[] { 0 }, new int[] { 4 });
+ }
+
+ private void checkCopy(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ try {
+ Fraction[][] sub = (reference == null) ?
+ new Fraction[1][1] :
+ new Fraction[reference.length][reference[0].length];
+ m.copySubMatrix(startRow, endRow, startColumn, endColumn, sub);
+ if (reference != null) {
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(reference), new Array2DRowFieldMatrix<Fraction>(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkCopy(FieldMatrix<Fraction> m, Fraction[][] reference,
+ int[] selectedRows, int[] selectedColumns) {
+ try {
+ Fraction[][] sub = (reference == null) ?
+ new Fraction[1][1] :
+ new Fraction[reference.length][reference[0].length];
+ m.copySubMatrix(selectedRows, selectedColumns, sub);
+ if (reference != null) {
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(reference), new Array2DRowFieldMatrix<Fraction>(sub));
+ } else {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (reference != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetRowMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mRow0 = new Array2DRowFieldMatrix<Fraction>(subRow0);
+ FieldMatrix<Fraction> mRow3 = new Array2DRowFieldMatrix<Fraction>(subRow3);
+ assertEquals("Row0", mRow0,
+ m.getRowMatrix(0));
+ assertEquals("Row3", mRow3,
+ m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mRow3 = new Array2DRowFieldMatrix<Fraction>(subRow3);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowMatrix(0, mRow3);
+ assertEquals(mRow3, m.getRowMatrix(0));
+ try {
+ m.setRowMatrix(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mColumn1 = new Array2DRowFieldMatrix<Fraction>(subColumn1);
+ FieldMatrix<Fraction> mColumn3 = new Array2DRowFieldMatrix<Fraction>(subColumn3);
+ assertEquals("Column1", mColumn1,
+ m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3,
+ m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnMatrix() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldMatrix<Fraction> mColumn3 = new Array2DRowFieldMatrix<Fraction>(subColumn3);
+ assertNotSame(mColumn3, m.getColumnMatrix(1));
+ m.setColumnMatrix(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnMatrix(1));
+ try {
+ m.setColumnMatrix(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowVector() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mRow0 = new ArrayFieldVector<Fraction>(subRow0[0]);
+ FieldVector<Fraction> mRow3 = new ArrayFieldVector<Fraction>(subRow3[0]);
+ assertEquals("Row0", mRow0, m.getRowVector(0));
+ assertEquals("Row3", mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowVector() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mRow3 = new ArrayFieldVector<Fraction>(subRow3[0]);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowVector(0, mRow3);
+ assertEquals(mRow3, m.getRowVector(0));
+ try {
+ m.setRowVector(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowVector(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), 5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnVector() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mColumn1 = columnToVector(subColumn1);
+ FieldVector<Fraction> mColumn3 = columnToVector(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnVector(1));
+ assertEquals("Column3", mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnVector() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ FieldVector<Fraction> mColumn3 = columnToVector(subColumn3);
+ assertNotSame(mColumn3, m.getColumnVector(1));
+ m.setColumnVector(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnVector(1));
+ try {
+ m.setColumnVector(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnVector(0, new ArrayFieldVector<Fraction>(FractionField.getInstance(), 5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private FieldVector<Fraction> columnToVector(Fraction[][] column) {
+ Fraction[] data = new Fraction[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayFieldVector<Fraction>(data, false);
+ }
+
+ public void testGetRow() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ checkArrays(subRow0[0], m.getRow(0));
+ checkArrays(subRow3[0], m.getRow(3));
+ try {
+ m.getRow(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRow(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRow() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ assertTrue(subRow3[0][0] != m.getRow(0)[0]);
+ m.setRow(0, subRow3[0]);
+ checkArrays(subRow3[0], m.getRow(0));
+ try {
+ m.setRow(-1, subRow3[0]);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRow(0, new Fraction[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumn() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ Fraction[] mColumn1 = columnToArray(subColumn1);
+ Fraction[] mColumn3 = columnToArray(subColumn3);
+ checkArrays(mColumn1, m.getColumn(1));
+ checkArrays(mColumn3, m.getColumn(3));
+ try {
+ m.getColumn(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumn(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumn() {
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(subTestData);
+ Fraction[] mColumn3 = columnToArray(subColumn3);
+ assertTrue(mColumn3[0] != m.getColumn(1)[0]);
+ m.setColumn(1, mColumn3);
+ checkArrays(mColumn3, m.getColumn(1));
+ try {
+ m.setColumn(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumn(0, new Fraction[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private Fraction[] columnToArray(Fraction[][] column) {
+ Fraction[] data = new Fraction[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return data;
+ }
+
+ private void checkArrays(Fraction[] expected, Fraction[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ Array2DRowFieldMatrix<Fraction> m1 = (Array2DRowFieldMatrix<Fraction>) m.copy();
+ Array2DRowFieldMatrix<Fraction> mt = (Array2DRowFieldMatrix<Fraction>) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new Array2DRowFieldMatrix<Fraction>(bigSingular)));
+ }
+
+ public void testToString() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ assertEquals("Array2DRowFieldMatrix{{1,2,3},{2,5,3},{1,0,8}}", m.toString());
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance());
+ assertEquals("Array2DRowFieldMatrix{}", m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ m.setSubMatrix(detData2,1,1);
+ FieldMatrix<Fraction> expected = new Array2DRowFieldMatrix<Fraction>
+ (new Fraction[][] {
+ {new Fraction(1),new Fraction(2),new Fraction(3)},
+ {new Fraction(2),new Fraction(1),new Fraction(3)},
+ {new Fraction(1),new Fraction(2),new Fraction(4)}
+ });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2,0,0);
+ expected = new Array2DRowFieldMatrix<Fraction>
+ (new Fraction[][] {
+ {new Fraction(1),new Fraction(3),new Fraction(3)},
+ {new Fraction(2),new Fraction(4),new Fraction(3)},
+ {new Fraction(1),new Fraction(2),new Fraction(4)}
+ });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2,0,0);
+ expected = new Array2DRowFieldMatrix<Fraction>
+ (new Fraction[][] {
+ {new Fraction(3),new Fraction(4),new Fraction(5)},
+ {new Fraction(4),new Fraction(7),new Fraction(5)},
+ {new Fraction(3),new Fraction(2),new Fraction(10)}
+ });
+ assertEquals(expected, m);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData,1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData,-1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData,1,-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ Array2DRowFieldMatrix<Fraction> m2 = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance());
+ try {
+ m2.setSubMatrix(testData,0,1);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ try {
+ m2.setSubMatrix(testData,1,0);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new Fraction[][] {{new Fraction(1)}, {new Fraction(2), new Fraction(3)}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new Fraction[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testWalk() throws MatrixVisitorException {
+ int rows = 150;
+ int columns = 75;
+
+ FieldMatrix<Fraction> m =
+ new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInRowOrder(new SetVisitor());
+ GetVisitor getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInRowOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInColumnOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInColumnOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new Array2DRowFieldMatrix<Fraction>(FractionField.getInstance(), rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(new Fraction(0), m.getEntry(i, 0));
+ assertEquals(new Fraction(0), m.getEntry(i, columns - 1));
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(new Fraction(0), m.getEntry(0, j));
+ assertEquals(new Fraction(0), m.getEntry(rows - 1, j));
+ }
+
+ }
+
+ public void testSerial() {
+ Array2DRowFieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+ private static class SetVisitor extends DefaultFieldMatrixChangingVisitor<Fraction> {
+ public SetVisitor() {
+ super(Fraction.ZERO);
+ }
+ @Override
+ public Fraction visit(int i, int j, Fraction value) {
+ return new Fraction(i * 1024 + j, 1024);
+ }
+ }
+
+ private static class GetVisitor extends DefaultFieldMatrixPreservingVisitor<Fraction> {
+ private int count;
+ public GetVisitor() {
+ super(Fraction.ZERO);
+ count = 0;
+ }
+ @Override
+ public void visit(int i, int j, Fraction value) {
+ ++count;
+ assertEquals(new Fraction(i * 1024 + j, 1024), value);
+ }
+ public int getCount() {
+ return count;
+ }
+ }
+
+ //--------------- -----------------Protected methods
+
+ /** extracts the l and u matrices from compact lu representation */
+ protected void splitLU(FieldMatrix<Fraction> lu,
+ Fraction[][] lowerData,
+ Fraction[][] upperData)
+ throws InvalidMatrixException {
+ if (!lu.isSquare() ||
+ lowerData.length != lowerData[0].length ||
+ upperData.length != upperData[0].length ||
+ lowerData.length != upperData.length ||
+ lowerData.length != lu.getRowDimension()) {
+ throw new InvalidMatrixException("incorrect dimensions");
+ }
+ int n = lu.getRowDimension();
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (j < i) {
+ lowerData[i][j] = lu.getEntry(i, j);
+ upperData[i][j] = Fraction.ZERO;
+ } else if (i == j) {
+ lowerData[i][j] = Fraction.ONE;
+ upperData[i][j] = lu.getEntry(i, j);
+ } else {
+ lowerData[i][j] = Fraction.ZERO;
+ upperData[i][j] = lu.getEntry(i, j);
+ }
+ }
+ }
+ }
+
+ /** Returns the result of applying the given row permutation to the matrix */
+ protected FieldMatrix<Fraction> permuteRows(FieldMatrix<Fraction> matrix, int[] permutation) {
+ if (!matrix.isSquare() || matrix.getRowDimension() != permutation.length) {
+ throw new IllegalArgumentException("dimension mismatch");
+ }
+ int n = matrix.getRowDimension();
+ int m = matrix.getColumnDimension();
+ Fraction out[][] = new Fraction[m][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ out[i][j] = matrix.getEntry(permutation[i], j);
+ }
+ }
+ return new Array2DRowFieldMatrix<Fraction>(out);
+ }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/FrenchRealVectorFormatTest.java b/src/test/java/org/apache/commons/math/linear/FrenchRealVectorFormatTest.java
new file mode 100644
index 0000000..59d9880
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/FrenchRealVectorFormatTest.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.linear;
+
+import java.util.Locale;
+
+
+public class FrenchRealVectorFormatTest extends RealVectorFormatAbstractTest {
+
+ @Override
+ protected char getDecimalCharacter() {
+ return ',';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.FRENCH;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/InvalidMatrixExceptionTest.java b/src/test/java/org/apache/commons/math/linear/InvalidMatrixExceptionTest.java
new file mode 100644
index 0000000..c79c5d4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/InvalidMatrixExceptionTest.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.linear;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class InvalidMatrixExceptionTest extends TestCase {
+
+ public void testConstructorMessage(){
+ String msg = "message";
+ InvalidMatrixException ex = new InvalidMatrixException(msg);
+ assertEquals(msg, ex.getMessage());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/LUDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/LUDecompositionImplTest.java
new file mode 100644
index 0000000..ecea0a2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/LUDecompositionImplTest.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import junit.framework.TestCase;
+
+public class LUDecompositionImplTest extends TestCase {
+ private double[][] testData = {
+ { 1.0, 2.0, 3.0},
+ { 2.0, 5.0, 3.0},
+ { 1.0, 0.0, 8.0}
+ };
+ private double[][] testDataMinus = {
+ { -1.0, -2.0, -3.0},
+ { -2.0, -5.0, -3.0},
+ { -1.0, 0.0, -8.0}
+ };
+ private double[][] luData = {
+ { 2.0, 3.0, 3.0 },
+ { 0.0, 5.0, 7.0 },
+ { 6.0, 9.0, 8.0 }
+ };
+
+ // singular matrices
+ private double[][] singular = {
+ { 2.0, 3.0 },
+ { 2.0, 3.0 }
+ };
+ private double[][] bigSingular = {
+ { 1.0, 2.0, 3.0, 4.0 },
+ { 2.0, 5.0, 3.0, 4.0 },
+ { 7.0, 3.0, 256.0, 1930.0 },
+ { 3.0, 7.0, 6.0, 8.0 }
+ }; // 4th row = 1st + 2nd
+
+ private static final double entryTolerance = 10e-16;
+
+ private static final double normTolerance = 10e-14;
+
+ public LUDecompositionImplTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ LUDecomposition LU = new LUDecompositionImpl(matrix);
+ assertEquals(testData.length, LU.getL().getRowDimension());
+ assertEquals(testData.length, LU.getL().getColumnDimension());
+ assertEquals(testData.length, LU.getU().getRowDimension());
+ assertEquals(testData.length, LU.getU().getColumnDimension());
+ assertEquals(testData.length, LU.getP().getRowDimension());
+ assertEquals(testData.length, LU.getP().getColumnDimension());
+
+ }
+
+ /** test non-square matrix */
+ public void testNonSquare() {
+ try {
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(new double[3][2]));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ }
+
+ /** test PA = LU */
+ public void testPAEqualLU() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ LUDecomposition lu = new LUDecompositionImpl(matrix);
+ RealMatrix l = lu.getL();
+ RealMatrix u = lu.getU();
+ RealMatrix p = lu.getP();
+ double norm = l.multiply(u).subtract(p.multiply(matrix)).getNorm();
+ assertEquals(0, norm, normTolerance);
+
+ matrix = MatrixUtils.createRealMatrix(testDataMinus);
+ lu = new LUDecompositionImpl(matrix);
+ l = lu.getL();
+ u = lu.getU();
+ p = lu.getP();
+ norm = l.multiply(u).subtract(p.multiply(matrix)).getNorm();
+ assertEquals(0, norm, normTolerance);
+
+ matrix = MatrixUtils.createRealIdentityMatrix(17);
+ lu = new LUDecompositionImpl(matrix);
+ l = lu.getL();
+ u = lu.getU();
+ p = lu.getP();
+ norm = l.multiply(u).subtract(p.multiply(matrix)).getNorm();
+ assertEquals(0, norm, normTolerance);
+
+ matrix = MatrixUtils.createRealMatrix(singular);
+ lu = new LUDecompositionImpl(matrix);
+ assertFalse(lu.getSolver().isNonSingular());
+ assertNull(lu.getL());
+ assertNull(lu.getU());
+ assertNull(lu.getP());
+
+ matrix = MatrixUtils.createRealMatrix(bigSingular);
+ lu = new LUDecompositionImpl(matrix);
+ assertFalse(lu.getSolver().isNonSingular());
+ assertNull(lu.getL());
+ assertNull(lu.getU());
+ assertNull(lu.getP());
+
+ }
+
+ /** test that L is lower triangular with unit diagonal */
+ public void testLLowerTriangular() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ RealMatrix l = new LUDecompositionImpl(matrix).getL();
+ for (int i = 0; i < l.getRowDimension(); i++) {
+ assertEquals(l.getEntry(i, i), 1, entryTolerance);
+ for (int j = i + 1; j < l.getColumnDimension(); j++) {
+ assertEquals(l.getEntry(i, j), 0, entryTolerance);
+ }
+ }
+ }
+
+ /** test that U is upper triangular */
+ public void testUUpperTriangular() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ RealMatrix u = new LUDecompositionImpl(matrix).getU();
+ for (int i = 0; i < u.getRowDimension(); i++) {
+ for (int j = 0; j < i; j++) {
+ assertEquals(u.getEntry(i, j), 0, entryTolerance);
+ }
+ }
+ }
+
+ /** test that P is a permutation matrix */
+ public void testPPermutation() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData);
+ RealMatrix p = new LUDecompositionImpl(matrix).getP();
+
+ RealMatrix ppT = p.multiply(p.transpose());
+ RealMatrix id = MatrixUtils.createRealIdentityMatrix(p.getRowDimension());
+ assertEquals(0, ppT.subtract(id).getNorm(), normTolerance);
+
+ for (int i = 0; i < p.getRowDimension(); i++) {
+ int zeroCount = 0;
+ int oneCount = 0;
+ int otherCount = 0;
+ for (int j = 0; j < p.getColumnDimension(); j++) {
+ final double e = p.getEntry(i, j);
+ if (e == 0) {
+ ++zeroCount;
+ } else if (e == 1) {
+ ++oneCount;
+ } else {
+ ++otherCount;
+ }
+ }
+ assertEquals(p.getColumnDimension() - 1, zeroCount);
+ assertEquals(1, oneCount);
+ assertEquals(0, otherCount);
+ }
+
+ for (int j = 0; j < p.getColumnDimension(); j++) {
+ int zeroCount = 0;
+ int oneCount = 0;
+ int otherCount = 0;
+ for (int i = 0; i < p.getRowDimension(); i++) {
+ final double e = p.getEntry(i, j);
+ if (e == 0) {
+ ++zeroCount;
+ } else if (e == 1) {
+ ++oneCount;
+ } else {
+ ++otherCount;
+ }
+ }
+ assertEquals(p.getRowDimension() - 1, zeroCount);
+ assertEquals(1, oneCount);
+ assertEquals(0, otherCount);
+ }
+
+ }
+
+
+ /** test singular */
+ public void testSingular() {
+ LUDecomposition lu =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData));
+ assertTrue(lu.getSolver().isNonSingular());
+ lu = new LUDecompositionImpl(MatrixUtils.createRealMatrix(singular));
+ assertFalse(lu.getSolver().isNonSingular());
+ lu = new LUDecompositionImpl(MatrixUtils.createRealMatrix(bigSingular));
+ assertFalse(lu.getSolver().isNonSingular());
+ }
+
+ /** test matrices values */
+ public void testMatricesValues1() {
+ LUDecomposition lu =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData));
+ RealMatrix lRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 0.0, 0.0 },
+ { 0.5, 1.0, 0.0 },
+ { 0.5, 0.2, 1.0 }
+ });
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 2.0, 5.0, 3.0 },
+ { 0.0, -2.5, 6.5 },
+ { 0.0, 0.0, 0.2 }
+ });
+ RealMatrix pRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0 }
+ });
+ int[] pivotRef = { 1, 2, 0 };
+
+ // check values against known references
+ RealMatrix l = lu.getL();
+ assertEquals(0, l.subtract(lRef).getNorm(), 1.0e-13);
+ RealMatrix u = lu.getU();
+ assertEquals(0, u.subtract(uRef).getNorm(), 1.0e-13);
+ RealMatrix p = lu.getP();
+ assertEquals(0, p.subtract(pRef).getNorm(), 1.0e-13);
+ int[] pivot = lu.getPivot();
+ for (int i = 0; i < pivotRef.length; ++i) {
+ assertEquals(pivotRef[i], pivot[i]);
+ }
+
+ // check the same cached instance is returned the second time
+ assertTrue(l == lu.getL());
+ assertTrue(u == lu.getU());
+ assertTrue(p == lu.getP());
+
+ }
+
+ /** test matrices values */
+ public void testMatricesValues2() {
+ LUDecomposition lu =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(luData));
+ RealMatrix lRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 1.0 / 3.0, 0.0, 1.0 }
+ });
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 6.0, 9.0, 8.0 },
+ { 0.0, 5.0, 7.0 },
+ { 0.0, 0.0, 1.0 / 3.0 }
+ });
+ RealMatrix pRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0 },
+ { 1.0, 0.0, 0.0 }
+ });
+ int[] pivotRef = { 2, 1, 0 };
+
+ // check values against known references
+ RealMatrix l = lu.getL();
+ assertEquals(0, l.subtract(lRef).getNorm(), 1.0e-13);
+ RealMatrix u = lu.getU();
+ assertEquals(0, u.subtract(uRef).getNorm(), 1.0e-13);
+ RealMatrix p = lu.getP();
+ assertEquals(0, p.subtract(pRef).getNorm(), 1.0e-13);
+ int[] pivot = lu.getPivot();
+ for (int i = 0; i < pivotRef.length; ++i) {
+ assertEquals(pivotRef[i], pivot[i]);
+ }
+
+ // check the same cached instance is returned the second time
+ assertTrue(l == lu.getL());
+ assertTrue(u == lu.getU());
+ assertTrue(p == lu.getP());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/LUSolverTest.java b/src/test/java/org/apache/commons/math/linear/LUSolverTest.java
new file mode 100644
index 0000000..54da984
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/LUSolverTest.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+public class LUSolverTest extends TestCase {
+ private double[][] testData = {
+ { 1.0, 2.0, 3.0},
+ { 2.0, 5.0, 3.0},
+ { 1.0, 0.0, 8.0}
+ };
+ private double[][] luData = {
+ { 2.0, 3.0, 3.0 },
+ { 0.0, 5.0, 7.0 },
+ { 6.0, 9.0, 8.0 }
+ };
+
+ // singular matrices
+ private double[][] singular = {
+ { 2.0, 3.0 },
+ { 2.0, 3.0 }
+ };
+ private double[][] bigSingular = {
+ { 1.0, 2.0, 3.0, 4.0 },
+ { 2.0, 5.0, 3.0, 4.0 },
+ { 7.0, 3.0, 256.0, 1930.0 },
+ { 3.0, 7.0, 6.0, 8.0 }
+ }; // 4th row = 1st + 2nd
+
+ public LUSolverTest(String name) {
+ super(name);
+ }
+
+ /** test threshold impact */
+ public void testThreshold() {
+ final RealMatrix matrix = MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 2.0, 3.0},
+ { 2.0, 5.0, 3.0},
+ { 4.000001, 9.0, 9.0}
+ });
+ assertFalse(new LUDecompositionImpl(matrix, 1.0e-5).getSolver().isNonSingular());
+ assertTrue(new LUDecompositionImpl(matrix, 1.0e-10).getSolver().isNonSingular());
+ }
+
+ /** test singular */
+ public void testSingular() {
+ DecompositionSolver solver =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
+ assertTrue(solver.isNonSingular());
+ solver = new LUDecompositionImpl(MatrixUtils.createRealMatrix(singular)).getSolver();
+ assertFalse(solver.isNonSingular());
+ solver = new LUDecompositionImpl(MatrixUtils.createRealMatrix(bigSingular)).getSolver();
+ assertFalse(solver.isNonSingular());
+ }
+
+ /** test solve dimension errors */
+ public void testSolveDimensionErrors() {
+ DecompositionSolver solver =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
+ try {
+ solver.solve(b);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test solve singularity errors */
+ public void testSolveSingularityErrors() {
+ DecompositionSolver solver =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(singular)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
+ try {
+ solver.solve(b);
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumnVector(0));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ try {
+ solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ }
+
+ /** test solve */
+ public void testSolve() {
+ DecompositionSolver solver =
+ new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 0 }, { 2, -5 }, { 3, 1 }
+ });
+ RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 19, -71 }, { -6, 22 }, { -2, 9 }
+ });
+
+ // using RealMatrix
+ assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), 1.0e-13);
+
+ // using double[]
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ new ArrayRealVector(solver.solve(b.getColumn(i))).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using ArrayRealVector
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ assertEquals(0,
+ solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using RealVector with an alternate implementation
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ ArrayRealVectorTest.RealVectorTestImpl v =
+ new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
+ assertEquals(0,
+ solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ }
+
+ /** test determinant */
+ public void testDeterminant() {
+ assertEquals( -1, getDeterminant(MatrixUtils.createRealMatrix(testData)), 1.0e-15);
+ assertEquals(-10, getDeterminant(MatrixUtils.createRealMatrix(luData)), 1.0e-14);
+ assertEquals( 0, getDeterminant(MatrixUtils.createRealMatrix(singular)), 1.0e-17);
+ assertEquals( 0, getDeterminant(MatrixUtils.createRealMatrix(bigSingular)), 1.0e-10);
+ }
+
+ private double getDeterminant(RealMatrix m) {
+ return new LUDecompositionImpl(m).getDeterminant();
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/MatrixIndexExceptionTest.java b/src/test/java/org/apache/commons/math/linear/MatrixIndexExceptionTest.java
new file mode 100644
index 0000000..f56d838
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/MatrixIndexExceptionTest.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.linear;
+
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class MatrixIndexExceptionTest extends TestCase {
+
+ /**
+ *
+ */
+ public void testParameter(){
+ MatrixIndexException ex = new MatrixIndexException(LocalizedFormats.INDEX_OUT_OF_RANGE, 12, 0, 5);
+ assertEquals(12, ex.getArguments()[0]);
+ assertEquals(0, ex.getArguments()[1]);
+ assertEquals(5, ex.getArguments()[2]);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/MatrixUtilsTest.java b/src/test/java/org/apache/commons/math/linear/MatrixUtilsTest.java
new file mode 100644
index 0000000..095ef31
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/MatrixUtilsTest.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.fraction.BigFraction;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionConversionException;
+import org.apache.commons.math.fraction.FractionField;
+
+/**
+ * Test cases for the {@link MatrixUtils} class.
+ *
+ * @version $Revision: 1003899 $ $Date: 2010-10-03 00:06:55 +0200 (dim. 03 oct. 2010) $
+ */
+
+public final class MatrixUtilsTest extends TestCase {
+
+ protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
+ protected double[][] nullMatrix = null;
+ protected double[] row = {1,2,3};
+ protected BigDecimal[] bigRow =
+ {new BigDecimal(1),new BigDecimal(2),new BigDecimal(3)};
+ protected String[] stringRow = {"1", "2", "3"};
+ protected Fraction[] fractionRow =
+ {new Fraction(1),new Fraction(2),new Fraction(3)};
+ protected double[][] rowMatrix = {{1,2,3}};
+ protected BigDecimal[][] bigRowMatrix =
+ {{new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)}};
+ protected String[][] stringRowMatrix = {{"1", "2", "3"}};
+ protected Fraction[][] fractionRowMatrix =
+ {{new Fraction(1), new Fraction(2), new Fraction(3)}};
+ protected double[] col = {0,4,6};
+ protected BigDecimal[] bigCol =
+ {new BigDecimal(0),new BigDecimal(4),new BigDecimal(6)};
+ protected String[] stringCol = {"0","4","6"};
+ protected Fraction[] fractionCol =
+ {new Fraction(0),new Fraction(4),new Fraction(6)};
+ protected double[] nullDoubleArray = null;
+ protected double[][] colMatrix = {{0},{4},{6}};
+ protected BigDecimal[][] bigColMatrix =
+ {{new BigDecimal(0)},{new BigDecimal(4)},{new BigDecimal(6)}};
+ protected String[][] stringColMatrix = {{"0"}, {"4"}, {"6"}};
+ protected Fraction[][] fractionColMatrix =
+ {{new Fraction(0)},{new Fraction(4)},{new Fraction(6)}};
+
+ public MatrixUtilsTest(String name) {
+ super(name);
+ }
+
+
+ public void testCreateRealMatrix() {
+ assertEquals(new BlockRealMatrix(testData),
+ MatrixUtils.createRealMatrix(testData));
+ try {
+ MatrixUtils.createRealMatrix(new double[][] {{1}, {1,2}}); // ragged
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createRealMatrix(new double[][] {{}, {}}); // no columns
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createRealMatrix(null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testcreateFieldMatrix() {
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(asFraction(testData)),
+ MatrixUtils.createFieldMatrix(asFraction(testData)));
+ assertEquals(new Array2DRowFieldMatrix<Fraction>(fractionColMatrix),
+ MatrixUtils.createFieldMatrix(fractionColMatrix));
+ try {
+ MatrixUtils.createFieldMatrix(asFraction(new double[][] {{1}, {1,2}})); // ragged
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createFieldMatrix(asFraction(new double[][] {{}, {}})); // no columns
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createFieldMatrix((Fraction[][])null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ @Deprecated
+ public void testCreateBigMatrix() {
+ assertEquals(new BigMatrixImpl(testData),
+ MatrixUtils.createBigMatrix(testData));
+ assertEquals(new BigMatrixImpl(BigMatrixImplTest.asBigDecimal(testData), true),
+ MatrixUtils.createBigMatrix(BigMatrixImplTest.asBigDecimal(testData), false));
+ assertEquals(new BigMatrixImpl(BigMatrixImplTest.asBigDecimal(testData), false),
+ MatrixUtils.createBigMatrix(BigMatrixImplTest.asBigDecimal(testData), true));
+ assertEquals(new BigMatrixImpl(bigColMatrix),
+ MatrixUtils.createBigMatrix(bigColMatrix));
+ assertEquals(new BigMatrixImpl(stringColMatrix),
+ MatrixUtils.createBigMatrix(stringColMatrix));
+ try {
+ MatrixUtils.createBigMatrix(new double[][] {{1}, {1,2}}); // ragged
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createBigMatrix(new double[][] {{}, {}}); // no columns
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createBigMatrix(nullMatrix); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testCreateRowRealMatrix() {
+ assertEquals(MatrixUtils.createRowRealMatrix(row),
+ new BlockRealMatrix(rowMatrix));
+ try {
+ MatrixUtils.createRowRealMatrix(new double[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createRowRealMatrix(null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testCreateRowFieldMatrix() {
+ assertEquals(MatrixUtils.createRowFieldMatrix(asFraction(row)),
+ new Array2DRowFieldMatrix<Fraction>(asFraction(rowMatrix)));
+ assertEquals(MatrixUtils.createRowFieldMatrix(fractionRow),
+ new Array2DRowFieldMatrix<Fraction>(fractionRowMatrix));
+ try {
+ MatrixUtils.createRowFieldMatrix(new Fraction[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createRowFieldMatrix((Fraction[]) null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ @Deprecated
+ public void testCreateRowBigMatrix() {
+ assertEquals(MatrixUtils.createRowBigMatrix(row),
+ new BigMatrixImpl(rowMatrix));
+ assertEquals(MatrixUtils.createRowBigMatrix(bigRow),
+ new BigMatrixImpl(bigRowMatrix));
+ assertEquals(MatrixUtils.createRowBigMatrix(stringRow),
+ new BigMatrixImpl(stringRowMatrix));
+ try {
+ MatrixUtils.createRowBigMatrix(new double[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createRowBigMatrix(nullDoubleArray); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testCreateColumnRealMatrix() {
+ assertEquals(MatrixUtils.createColumnRealMatrix(col),
+ new BlockRealMatrix(colMatrix));
+ try {
+ MatrixUtils.createColumnRealMatrix(new double[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createColumnRealMatrix(null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testCreateColumnFieldMatrix() {
+ assertEquals(MatrixUtils.createColumnFieldMatrix(asFraction(col)),
+ new Array2DRowFieldMatrix<Fraction>(asFraction(colMatrix)));
+ assertEquals(MatrixUtils.createColumnFieldMatrix(fractionCol),
+ new Array2DRowFieldMatrix<Fraction>(fractionColMatrix));
+
+ try {
+ MatrixUtils.createColumnFieldMatrix(new Fraction[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createColumnFieldMatrix((Fraction[]) null); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ @Deprecated
+ public void testCreateColumnBigMatrix() {
+ assertEquals(MatrixUtils.createColumnBigMatrix(col),
+ new BigMatrixImpl(colMatrix));
+ assertEquals(MatrixUtils.createColumnBigMatrix(bigCol),
+ new BigMatrixImpl(bigColMatrix));
+ assertEquals(MatrixUtils.createColumnBigMatrix(stringCol),
+ new BigMatrixImpl(stringColMatrix));
+
+ try {
+ MatrixUtils.createColumnBigMatrix(new double[] {}); // empty
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ MatrixUtils.createColumnBigMatrix(nullDoubleArray); // null
+ fail("Expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Verifies that the matrix is an identity matrix
+ */
+ protected void checkIdentityMatrix(RealMatrix m) {
+ for (int i = 0; i < m.getRowDimension(); i++) {
+ for (int j =0; j < m.getColumnDimension(); j++) {
+ if (i == j) {
+ assertEquals(m.getEntry(i, j), 1d, 0);
+ } else {
+ assertEquals(m.getEntry(i, j), 0d, 0);
+ }
+ }
+ }
+ }
+
+ public void testCreateIdentityMatrix() {
+ checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(3));
+ checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(2));
+ checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(1));
+ try {
+ MatrixUtils.createRealIdentityMatrix(0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Verifies that the matrix is an identity matrix
+ */
+ protected void checkIdentityFieldMatrix(FieldMatrix<Fraction> m) {
+ for (int i = 0; i < m.getRowDimension(); i++) {
+ for (int j =0; j < m.getColumnDimension(); j++) {
+ if (i == j) {
+ assertEquals(m.getEntry(i, j), Fraction.ONE);
+ } else {
+ assertEquals(m.getEntry(i, j), Fraction.ZERO);
+ }
+ }
+ }
+ }
+
+ public void testcreateFieldIdentityMatrix() {
+ checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(FractionField.getInstance(), 3));
+ checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(FractionField.getInstance(), 2));
+ checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(FractionField.getInstance(), 1));
+ try {
+ MatrixUtils.createRealIdentityMatrix(0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBigFractionConverter() {
+ BigFraction[][] bfData = {
+ { new BigFraction(1), new BigFraction(2), new BigFraction(3) },
+ { new BigFraction(2), new BigFraction(5), new BigFraction(3) },
+ { new BigFraction(1), new BigFraction(0), new BigFraction(8) }
+ };
+ FieldMatrix<BigFraction> m = new Array2DRowFieldMatrix<BigFraction>(bfData, false);
+ RealMatrix converted = MatrixUtils.bigFractionMatrixToRealMatrix(m);
+ RealMatrix reference = new Array2DRowRealMatrix(testData, false);
+ assertEquals(0.0, converted.subtract(reference).getNorm(), 0.0);
+ }
+
+ public void testFractionConverter() {
+ Fraction[][] fData = {
+ { new Fraction(1), new Fraction(2), new Fraction(3) },
+ { new Fraction(2), new Fraction(5), new Fraction(3) },
+ { new Fraction(1), new Fraction(0), new Fraction(8) }
+ };
+ FieldMatrix<Fraction> m = new Array2DRowFieldMatrix<Fraction>(fData, false);
+ RealMatrix converted = MatrixUtils.fractionMatrixToRealMatrix(m);
+ RealMatrix reference = new Array2DRowRealMatrix(testData, false);
+ assertEquals(0.0, converted.subtract(reference).getNorm(), 0.0);
+ }
+
+ public static final Fraction[][] asFraction(double[][] data) {
+ Fraction d[][] = new Fraction[data.length][];
+ try {
+ for (int i = 0; i < data.length; ++i) {
+ double[] dataI = data[i];
+ Fraction[] dI = new Fraction[dataI.length];
+ for (int j = 0; j < dataI.length; ++j) {
+ dI[j] = new Fraction(dataI[j]);
+ }
+ d[i] = dI;
+ }
+ } catch (FractionConversionException fce) {
+ fail(fce.getMessage());
+ }
+ return d;
+ }
+
+ public static final Fraction[] asFraction(double[] data) {
+ Fraction d[] = new Fraction[data.length];
+ try {
+ for (int i = 0; i < data.length; ++i) {
+ d[i] = new Fraction(data[i]);
+ }
+ } catch (FractionConversionException fce) {
+ fail(fce.getMessage());
+ }
+ return d;
+ }
+
+ /**
+ * Verifies that the matrix is an identity matrix
+ */
+ @Deprecated
+ protected void checkIdentityBigMatrix(BigMatrix m) {
+ for (int i = 0; i < m.getRowDimension(); i++) {
+ for (int j =0; j < m.getColumnDimension(); j++) {
+ if (i == j) {
+ assertEquals(m.getEntry(i, j), BigMatrixImpl.ONE);
+ } else {
+ assertEquals(m.getEntry(i, j), BigMatrixImpl.ZERO);
+ }
+ }
+ }
+ }
+
+ @Deprecated
+ public void testCreateBigIdentityMatrix() {
+ checkIdentityBigMatrix(MatrixUtils.createBigIdentityMatrix(3));
+ checkIdentityBigMatrix(MatrixUtils.createBigIdentityMatrix(2));
+ checkIdentityBigMatrix(MatrixUtils.createBigIdentityMatrix(1));
+ try {
+ MatrixUtils.createRealIdentityMatrix(0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/QRDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/QRDecompositionImplTest.java
new file mode 100644
index 0000000..b3a203f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/QRDecompositionImplTest.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Random;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+import junit.framework.TestCase;
+
+public class QRDecompositionImplTest extends TestCase {
+ double[][] testData3x3NonSingular = {
+ { 12, -51, 4 },
+ { 6, 167, -68 },
+ { -4, 24, -41 }, };
+
+ double[][] testData3x3Singular = {
+ { 1, 4, 7, },
+ { 2, 5, 8, },
+ { 3, 6, 9, }, };
+
+ double[][] testData3x4 = {
+ { 12, -51, 4, 1 },
+ { 6, 167, -68, 2 },
+ { -4, 24, -41, 3 }, };
+
+ double[][] testData4x3 = {
+ { 12, -51, 4, },
+ { 6, 167, -68, },
+ { -4, 24, -41, },
+ { -5, 34, 7, }, };
+
+ private static final double entryTolerance = 10e-16;
+
+ private static final double normTolerance = 10e-14;
+
+ public QRDecompositionImplTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions
+ * @throws MatrixVisitorException */
+ public void testDimensions() throws MatrixVisitorException {
+ checkDimension(MatrixUtils.createRealMatrix(testData3x3NonSingular));
+
+ checkDimension(MatrixUtils.createRealMatrix(testData4x3));
+
+ checkDimension(MatrixUtils.createRealMatrix(testData3x4));
+
+ Random r = new Random(643895747384642l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ checkDimension(createTestMatrix(r, p, q));
+ checkDimension(createTestMatrix(r, q, p));
+
+ }
+
+ private void checkDimension(RealMatrix m) {
+ int rows = m.getRowDimension();
+ int columns = m.getColumnDimension();
+ QRDecomposition qr = new QRDecompositionImpl(m);
+ assertEquals(rows, qr.getQ().getRowDimension());
+ assertEquals(rows, qr.getQ().getColumnDimension());
+ assertEquals(rows, qr.getR().getRowDimension());
+ assertEquals(columns, qr.getR().getColumnDimension());
+ }
+
+ /** test A = QR
+ * @throws MatrixVisitorException */
+ public void testAEqualQR() throws MatrixVisitorException {
+ checkAEqualQR(MatrixUtils.createRealMatrix(testData3x3NonSingular));
+
+ checkAEqualQR(MatrixUtils.createRealMatrix(testData3x3Singular));
+
+ checkAEqualQR(MatrixUtils.createRealMatrix(testData3x4));
+
+ checkAEqualQR(MatrixUtils.createRealMatrix(testData4x3));
+
+ Random r = new Random(643895747384642l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ checkAEqualQR(createTestMatrix(r, p, q));
+
+ checkAEqualQR(createTestMatrix(r, q, p));
+
+ }
+
+ private void checkAEqualQR(RealMatrix m) {
+ QRDecomposition qr = new QRDecompositionImpl(m);
+ double norm = qr.getQ().multiply(qr.getR()).subtract(m).getNorm();
+ assertEquals(0, norm, normTolerance);
+ }
+
+ /** test the orthogonality of Q
+ * @throws MatrixVisitorException */
+ public void testQOrthogonal() throws MatrixVisitorException {
+ checkQOrthogonal(MatrixUtils.createRealMatrix(testData3x3NonSingular));
+
+ checkQOrthogonal(MatrixUtils.createRealMatrix(testData3x3Singular));
+
+ checkQOrthogonal(MatrixUtils.createRealMatrix(testData3x4));
+
+ checkQOrthogonal(MatrixUtils.createRealMatrix(testData4x3));
+
+ Random r = new Random(643895747384642l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ checkQOrthogonal(createTestMatrix(r, p, q));
+
+ checkQOrthogonal(createTestMatrix(r, q, p));
+
+ }
+
+ private void checkQOrthogonal(RealMatrix m) {
+ QRDecomposition qr = new QRDecompositionImpl(m);
+ RealMatrix eye = MatrixUtils.createRealIdentityMatrix(m.getRowDimension());
+ double norm = qr.getQT().multiply(qr.getQ()).subtract(eye).getNorm();
+ assertEquals(0, norm, normTolerance);
+ }
+
+ /** test that R is upper triangular */
+ public void testRUpperTriangular() throws MatrixVisitorException {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData3x3NonSingular);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ matrix = MatrixUtils.createRealMatrix(testData3x3Singular);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ matrix = MatrixUtils.createRealMatrix(testData3x4);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ matrix = MatrixUtils.createRealMatrix(testData4x3);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ Random r = new Random(643895747384642l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ matrix = createTestMatrix(r, p, q);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ matrix = createTestMatrix(r, p, q);
+ checkUpperTriangular(new QRDecompositionImpl(matrix).getR());
+
+ }
+
+ private void checkUpperTriangular(RealMatrix m) throws MatrixVisitorException {
+ m.walkInOptimizedOrder(new DefaultRealMatrixPreservingVisitor() {
+ @Override
+ public void visit(int row, int column, double value) {
+ if (column < row) {
+ assertEquals(0.0, value, entryTolerance);
+ }
+ }
+ });
+ }
+
+ /** test that H is trapezoidal
+ * @throws MatrixVisitorException */
+ public void testHTrapezoidal() throws MatrixVisitorException {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testData3x3NonSingular);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ matrix = MatrixUtils.createRealMatrix(testData3x3Singular);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ matrix = MatrixUtils.createRealMatrix(testData3x4);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ matrix = MatrixUtils.createRealMatrix(testData4x3);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ Random r = new Random(643895747384642l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ matrix = createTestMatrix(r, p, q);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ matrix = createTestMatrix(r, p, q);
+ checkTrapezoidal(new QRDecompositionImpl(matrix).getH());
+
+ }
+
+ private void checkTrapezoidal(RealMatrix m) throws MatrixVisitorException {
+ m.walkInOptimizedOrder(new DefaultRealMatrixPreservingVisitor() {
+ @Override
+ public void visit(int row, int column, double value) {
+ if (column > row) {
+ assertEquals(0.0, value, entryTolerance);
+ }
+ }
+ });
+ }
+ /** test matrices values */
+ public void testMatricesValues() {
+ QRDecomposition qr =
+ new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3NonSingular));
+ RealMatrix qRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -12.0 / 14.0, 69.0 / 175.0, -58.0 / 175.0 },
+ { -6.0 / 14.0, -158.0 / 175.0, 6.0 / 175.0 },
+ { 4.0 / 14.0, -30.0 / 175.0, -165.0 / 175.0 }
+ });
+ RealMatrix rRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -14.0, -21.0, 14.0 },
+ { 0.0, -175.0, 70.0 },
+ { 0.0, 0.0, 35.0 }
+ });
+ RealMatrix hRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 26.0 / 14.0, 0.0, 0.0 },
+ { 6.0 / 14.0, 648.0 / 325.0, 0.0 },
+ { -4.0 / 14.0, 36.0 / 325.0, 2.0 }
+ });
+
+ // check values against known references
+ RealMatrix q = qr.getQ();
+ assertEquals(0, q.subtract(qRef).getNorm(), 1.0e-13);
+ RealMatrix qT = qr.getQT();
+ assertEquals(0, qT.subtract(qRef.transpose()).getNorm(), 1.0e-13);
+ RealMatrix r = qr.getR();
+ assertEquals(0, r.subtract(rRef).getNorm(), 1.0e-13);
+ RealMatrix h = qr.getH();
+ assertEquals(0, h.subtract(hRef).getNorm(), 1.0e-13);
+
+ // check the same cached instance is returned the second time
+ assertTrue(q == qr.getQ());
+ assertTrue(r == qr.getR());
+ assertTrue(h == qr.getH());
+
+ }
+
+ private RealMatrix createTestMatrix(final Random r, final int rows, final int columns) throws MatrixVisitorException {
+ RealMatrix m = MatrixUtils.createRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor(){
+ @Override
+ public double visit(int row, int column, double value) {
+ return 2.0 * r.nextDouble() - 1.0;
+ }
+ });
+ return m;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/QRSolverTest.java b/src/test/java/org/apache/commons/math/linear/QRSolverTest.java
new file mode 100644
index 0000000..ccedc7a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/QRSolverTest.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.linear;
+
+import java.util.Random;
+
+import org.apache.commons.math.linear.MatrixVisitorException;
+
+import junit.framework.TestCase;
+
+public class QRSolverTest extends TestCase {
+ double[][] testData3x3NonSingular = {
+ { 12, -51, 4 },
+ { 6, 167, -68 },
+ { -4, 24, -41 }
+ };
+
+ double[][] testData3x3Singular = {
+ { 1, 2, 2 },
+ { 2, 4, 6 },
+ { 4, 8, 12 }
+ };
+
+ double[][] testData3x4 = {
+ { 12, -51, 4, 1 },
+ { 6, 167, -68, 2 },
+ { -4, 24, -41, 3 }
+ };
+
+ double[][] testData4x3 = {
+ { 12, -51, 4 },
+ { 6, 167, -68 },
+ { -4, 24, -41 },
+ { -5, 34, 7 }
+ };
+
+ public QRSolverTest(String name) {
+ super(name);
+ }
+
+ /** test rank */
+ public void testRank() {
+ DecompositionSolver solver =
+ new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3NonSingular)).getSolver();
+ assertTrue(solver.isNonSingular());
+
+ solver = new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3Singular)).getSolver();
+ assertFalse(solver.isNonSingular());
+
+ solver = new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x4)).getSolver();
+ assertTrue(solver.isNonSingular());
+
+ solver = new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData4x3)).getSolver();
+ assertTrue(solver.isNonSingular());
+
+ }
+
+ /** test solve dimension errors */
+ public void testSolveDimensionErrors() {
+ DecompositionSolver solver =
+ new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3NonSingular)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
+ try {
+ solver.solve(b);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumnVector(0));
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test solve rank errors */
+ public void testSolveRankErrors() {
+ DecompositionSolver solver =
+ new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3Singular)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]);
+ try {
+ solver.solve(b);
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumnVector(0));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test solve */
+ public void testSolve() {
+ QRDecomposition decomposition =
+ new QRDecompositionImpl(MatrixUtils.createRealMatrix(testData3x3NonSingular));
+ DecompositionSolver solver = decomposition.getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { -102, 12250 }, { 544, 24500 }, { 167, -36750 }
+ });
+ RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 2515 }, { 2, 422 }, { -3, 898 }
+ });
+
+ // using RealMatrix
+ assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), 2.0e-16 * xRef.getNorm());
+
+ // using double[]
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ final double[] x = solver.solve(b.getColumn(i));
+ final double error = new ArrayRealVector(x).subtract(xRef.getColumnVector(i)).getNorm();
+ assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
+ }
+
+ // using ArrayRealVector
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ final RealVector x = solver.solve(b.getColumnVector(i));
+ final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
+ assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
+ }
+
+ // using RealVector with an alternate implementation
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ ArrayRealVectorTest.RealVectorTestImpl v =
+ new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
+ final RealVector x = solver.solve(v);
+ final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
+ assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
+ }
+
+ }
+
+ public void testOverdetermined() throws MatrixVisitorException {
+ final Random r = new Random(5559252868205245l);
+ int p = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ RealMatrix a = createTestMatrix(r, p, q);
+ RealMatrix xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
+
+ // build a perturbed system: A.X + noise = B
+ RealMatrix b = a.multiply(xRef);
+ final double noise = 0.001;
+ b.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
+ @Override
+ public double visit(int row, int column, double value) {
+ return value * (1.0 + noise * (2 * r.nextDouble() - 1));
+ }
+ });
+
+ // despite perturbation, the least square solution should be pretty good
+ RealMatrix x = new QRDecompositionImpl(a).getSolver().solve(b);
+ assertEquals(0, x.subtract(xRef).getNorm(), 0.01 * noise * p * q);
+
+ }
+
+ public void testUnderdetermined() throws MatrixVisitorException {
+ final Random r = new Random(42185006424567123l);
+ int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
+ RealMatrix a = createTestMatrix(r, p, q);
+ RealMatrix xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
+ RealMatrix b = a.multiply(xRef);
+ RealMatrix x = new QRDecompositionImpl(a).getSolver().solve(b);
+
+ // too many equations, the system cannot be solved at all
+ assertTrue(x.subtract(xRef).getNorm() / (p * q) > 0.01);
+
+ // the last unknown should have been set to 0
+ assertEquals(0.0, x.getSubMatrix(p, q - 1, 0, x.getColumnDimension() - 1).getNorm());
+
+ }
+
+ private RealMatrix createTestMatrix(final Random r, final int rows, final int columns) throws MatrixVisitorException {
+ RealMatrix m = MatrixUtils.createRealMatrix(rows, columns);
+ m.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor(){
+ @Override
+ public double visit(int row, int column, double value) {
+ return 2.0 * r.nextDouble() - 1.0;
+ }
+ });
+ return m;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/RealMatrixImplTest.java b/src/test/java/org/apache/commons/math/linear/RealMatrixImplTest.java
new file mode 100644
index 0000000..2007ce1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/RealMatrixImplTest.java
@@ -0,0 +1,1008 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link RealMatrixImpl} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+ at Deprecated
+public final class RealMatrixImplTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected double[][] id = { {1d,0d,0d}, {0d,1d,0d}, {0d,0d,1d} };
+
+ // Test data for group operations
+ protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
+ protected double[][] testDataLU = {{2d, 5d, 3d}, {.5d, -2.5d, 6.5d}, {0.5d, 0.2d, .2d}};
+ protected double[][] testDataPlus2 = { {3d,4d,5d}, {4d,7d,5d}, {3d,2d,10d} };
+ protected double[][] testDataMinus = { {-1d,-2d,-3d}, {-2d,-5d,-3d},
+ {-1d,0d,-8d} };
+ protected double[] testDataRow1 = {1d,2d,3d};
+ protected double[] testDataCol3 = {3d,3d,8d};
+ protected double[][] testDataInv =
+ { {-40d,16d,9d}, {13d,-5d,-3d}, {5d,-2d,-1d} };
+ protected double[] preMultTest = {8,12,33};
+ protected double[][] testData2 ={ {1d,2d,3d}, {2d,5d,3d}};
+ protected double[][] testData2T = { {1d,2d}, {2d,5d}, {3d,3d}};
+ protected double[][] testDataPlusInv =
+ { {-39d,18d,12d}, {15d,0d,0d}, {6d,-2d,7d} };
+
+ // lu decomposition tests
+ protected double[][] luData = { {2d,3d,3d}, {0d,5d,7d}, {6d,9d,8d} };
+ protected double[][] luDataLUDecomposition = { {6d,9d,8d}, {0d,5d,7d},
+ {0.33333333333333,0d,0.33333333333333} };
+
+ // singular matrices
+ protected double[][] singular = { {2d,3d}, {2d,3d} };
+ protected double[][] bigSingular = {{1d,2d,3d,4d}, {2d,5d,3d,4d},
+ {7d,3d,256d,1930d}, {3d,7d,6d,8d}}; // 4th row = 1st + 2nd
+ protected double[][] detData = { {1d,2d,3d}, {4d,5d,6d}, {7d,8d,10d} };
+ protected double[][] detData2 = { {1d, 3d}, {2d, 4d}};
+
+ // vectors
+ protected double[] testVector = {1,2,3};
+ protected double[] testVector2 = {1,2,3,4};
+
+ // submatrix accessor tests
+ protected double[][] subTestData = {{1, 2, 3, 4}, {1.5, 2.5, 3.5, 4.5},
+ {2, 4, 6, 8}, {4, 5, 6, 7}};
+ // array selections
+ protected double[][] subRows02Cols13 = { {2, 4}, {4, 8}};
+ protected double[][] subRows03Cols12 = { {2, 3}, {5, 6}};
+ protected double[][] subRows03Cols123 = { {2, 3, 4} , {5, 6, 7}};
+ // effective permutations
+ protected double[][] subRows20Cols123 = { {4, 6, 8} , {2, 3, 4}};
+ protected double[][] subRows31Cols31 = {{7, 5}, {4.5, 2.5}};
+ // contiguous ranges
+ protected double[][] subRows01Cols23 = {{3,4} , {3.5, 4.5}};
+ protected double[][] subRows23Cols00 = {{2} , {4}};
+ protected double[][] subRows00Cols33 = {{4}};
+ // row matrices
+ protected double[][] subRow0 = {{1,2,3,4}};
+ protected double[][] subRow3 = {{4,5,6,7}};
+ // column matrices
+ protected double[][] subColumn1 = {{2}, {2.5}, {4}, {5}};
+ protected double[][] subColumn3 = {{4}, {4.5}, {8}, {7}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public RealMatrixImplTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(testData2);
+ assertEquals("testData row dimension",3,m.getRowDimension());
+ assertEquals("testData column dimension",3,m.getColumnDimension());
+ assertTrue("testData is square",m.isSquare());
+ assertEquals("testData2 row dimension",m2.getRowDimension(),2);
+ assertEquals("testData2 column dimension",m2.getColumnDimension(),3);
+ assertTrue("testData2 is not square",!m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ RealMatrixImpl m1 = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(m1.getData());
+ assertEquals(m2,m1);
+ RealMatrixImpl m3 = new RealMatrixImpl(testData);
+ RealMatrixImpl m4 = new RealMatrixImpl(m3.getData(), false);
+ assertEquals(m4,m3);
+ }
+
+ /** test add */
+ public void testAdd() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl mInv = new RealMatrixImpl(testDataInv);
+ RealMatrix mPlusMInv = m.add(mInv);
+ double[][] sumEntries = mPlusMInv.getData();
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ testDataPlusInv[row][col],sumEntries[row][col],
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test norm */
+ public void testNorm() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(testData2);
+ assertEquals("testData norm",14d,m.getNorm(),entryTolerance);
+ assertEquals("testData2 norm",7d,m2.getNorm(),entryTolerance);
+ }
+
+ /** test Frobenius norm */
+ public void testFrobeniusNorm() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(testData2);
+ assertEquals("testData Frobenius norm", FastMath.sqrt(117.0), m.getFrobeniusNorm(), entryTolerance);
+ assertEquals("testData2 Frobenius norm", FastMath.sqrt(52.0), m2.getFrobeniusNorm(), entryTolerance);
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m2 = new RealMatrixImpl(testDataInv);
+ TestUtils.assertEquals("m-n = m + -n",m.subtract(m2),
+ m2.scalarMultiply(-1d).add(m),entryTolerance);
+ try {
+ m.subtract(new RealMatrixImpl(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl mInv = new RealMatrixImpl(testDataInv);
+ RealMatrixImpl identity = new RealMatrixImpl(id);
+ RealMatrixImpl m2 = new RealMatrixImpl(testData2);
+ TestUtils.assertEquals("inverse multiply",m.multiply(mInv),
+ identity,entryTolerance);
+ TestUtils.assertEquals("inverse multiply",mInv.multiply(m),
+ identity,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m.multiply(identity),
+ m,entryTolerance);
+ TestUtils.assertEquals("identity multiply",identity.multiply(mInv),
+ mInv,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m2.multiply(identity),
+ m2,entryTolerance);
+ try {
+ m.multiply(new RealMatrixImpl(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ //Additional Test for RealMatrixImplTest.testMultiply
+
+ private double[][] d3 = new double[][] {{1,2,3,4},{5,6,7,8}};
+ private double[][] d4 = new double[][] {{1},{2},{3},{4}};
+ private double[][] d5 = new double[][] {{30},{70}};
+
+ public void testMultiply2() {
+ RealMatrix m3 = new RealMatrixImpl(d3);
+ RealMatrix m4 = new RealMatrixImpl(d4);
+ RealMatrix m5 = new RealMatrixImpl(d5);
+ TestUtils.assertEquals("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ RealMatrix m = new RealMatrixImpl(id);
+ assertEquals("identity trace",3d,m.getTrace(),entryTolerance);
+ m = new RealMatrixImpl(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ RealMatrix m = new RealMatrixImpl(testData);
+ TestUtils.assertEquals("scalar add",new RealMatrixImpl(testDataPlus2),
+ m.scalarAdd(2d),entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ RealMatrix m = new RealMatrixImpl(id);
+ TestUtils.assertEquals("identity operate", testVector,
+ m.operate(testVector), entryTolerance);
+ TestUtils.assertEquals("identity operate", testVector,
+ m.operate(new ArrayRealVector(testVector)).getData(), entryTolerance);
+ m = new RealMatrixImpl(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ RealMatrix a = new RealMatrixImpl(new double[][] {
+ { 1, 2 }, { 3, 4 }, { 5, 6 }
+ }, false);
+ double[] b = a.operate(new double[] { 1, 1 });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals( 3.0, b[0], 1.0e-12);
+ assertEquals( 7.0, b[1], 1.0e-12);
+ assertEquals(11.0, b[2], 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+ RealMatrix m = new RealMatrixImpl(testData);
+ RealMatrix mIT = new LUDecompositionImpl(m).getSolver().getInverse().transpose();
+ RealMatrix mTI = new LUDecompositionImpl(m.transpose()).getSolver().getInverse();
+ TestUtils.assertEquals("inverse-transpose", mIT, mTI, normTolerance);
+ m = new RealMatrixImpl(testData2);
+ RealMatrix mt = new RealMatrixImpl(testData2T);
+ TestUtils.assertEquals("transpose",mt,m.transpose(),normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ RealMatrix m = new RealMatrixImpl(testData);
+ TestUtils.assertEquals("premultiply", m.preMultiply(testVector),
+ preMultTest, normTolerance);
+ TestUtils.assertEquals("premultiply", m.preMultiply(new ArrayRealVector(testVector).getData()),
+ preMultTest, normTolerance);
+ m = new RealMatrixImpl(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ RealMatrix m3 = new RealMatrixImpl(d3);
+ RealMatrix m4 = new RealMatrixImpl(d4);
+ RealMatrix m5 = new RealMatrixImpl(d5);
+ TestUtils.assertEquals("m3*m4=m5", m4.preMultiply(m3), m5, entryTolerance);
+
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl mInv = new RealMatrixImpl(testDataInv);
+ RealMatrixImpl identity = new RealMatrixImpl(id);
+ TestUtils.assertEquals("inverse multiply",m.preMultiply(mInv),
+ identity,entryTolerance);
+ TestUtils.assertEquals("inverse multiply",mInv.preMultiply(m),
+ identity,entryTolerance);
+ TestUtils.assertEquals("identity multiply",m.preMultiply(identity),
+ m,entryTolerance);
+ TestUtils.assertEquals("identity multiply",identity.preMultiply(mInv),
+ mInv,entryTolerance);
+ try {
+ m.preMultiply(new RealMatrixImpl(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ RealMatrix m = new RealMatrixImpl(testData);
+ TestUtils.assertEquals("get row",m.getRow(0),testDataRow1,entryTolerance);
+ TestUtils.assertEquals("get col",m.getColumn(2),testDataCol3,entryTolerance);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ RealMatrix m = new RealMatrixImpl(testData);
+ assertEquals("get entry",m.getEntry(0,1),2d,entryTolerance);
+ try {
+ m.getEntry(10, 4);
+ fail ("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ double[][] matrixData = { {1d,2d,3d}, {2d,5d,3d}};
+ RealMatrix m = new RealMatrixImpl(matrixData);
+ // One more with three rows, two columns
+ double[][] matrixData2 = { {1d,2d}, {2d,5d}, {1d, 7d}};
+ RealMatrix n = new RealMatrixImpl(matrixData2);
+ // Now multiply m by n
+ RealMatrix p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ RealMatrix pInverse = new LUDecompositionImpl(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ double[][] coefficientsData = {{2, 3, -2}, {-1, 7, 6}, {4, -3, -5}};
+ RealMatrix coefficients = new RealMatrixImpl(coefficientsData);
+ double[] constants = {1, -2, 1};
+ double[] solution = new LUDecompositionImpl(coefficients).getSolver().solve(constants);
+ assertEquals(2 * solution[0] + 3 * solution[1] -2 * solution[2], constants[0], 1E-12);
+ assertEquals(-1 * solution[0] + 7 * solution[1] + 6 * solution[2], constants[1], 1E-12);
+ assertEquals(4 * solution[0] - 3 * solution[1] -5 * solution[2], constants[2], 1E-12);
+
+ }
+
+ // test submatrix accessors
+ public void testGetSubMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ checkGetSubMatrix(m, subRows23Cols00, 2 , 3 , 0, 0, false);
+ checkGetSubMatrix(m, subRows00Cols33, 0 , 0 , 3, 3, false);
+ checkGetSubMatrix(m, subRows01Cols23, 0 , 1 , 2, 3, false);
+ checkGetSubMatrix(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 }, false);
+ checkGetSubMatrix(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 }, false);
+ checkGetSubMatrix(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 }, false);
+ checkGetSubMatrix(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 }, false);
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkGetSubMatrix(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4, true);
+ checkGetSubMatrix(m, null, -1, 1, 2, 2, true);
+ checkGetSubMatrix(m, null, 1, 0, 2, 2, true);
+ checkGetSubMatrix(m, null, 1, 0, 2, 4, true);
+ checkGetSubMatrix(m, null, new int[] {}, new int[] { 0 }, true);
+ checkGetSubMatrix(m, null, new int[] { 0 }, new int[] { 4 }, true);
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn,
+ boolean mustFail) {
+ try {
+ RealMatrix sub = m.getSubMatrix(startRow, endRow, startColumn, endColumn);
+ assertEquals(new RealMatrixImpl(reference), sub);
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkGetSubMatrix(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns,
+ boolean mustFail) {
+ try {
+ RealMatrix sub = m.getSubMatrix(selectedRows, selectedColumns);
+ assertEquals(new RealMatrixImpl(reference), sub);
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ public void testCopySubMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ checkCopy(m, subRows23Cols00, 2 , 3 , 0, 0, false);
+ checkCopy(m, subRows00Cols33, 0 , 0 , 3, 3, false);
+ checkCopy(m, subRows01Cols23, 0 , 1 , 2, 3, false);
+ checkCopy(m, subRows02Cols13, new int[] { 0, 2 }, new int[] { 1, 3 }, false);
+ checkCopy(m, subRows03Cols12, new int[] { 0, 3 }, new int[] { 1, 2 }, false);
+ checkCopy(m, subRows03Cols123, new int[] { 0, 3 }, new int[] { 1, 2, 3 }, false);
+ checkCopy(m, subRows20Cols123, new int[] { 2, 0 }, new int[] { 1, 2, 3 }, false);
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+ checkCopy(m, subRows31Cols31, new int[] { 3, 1 }, new int[] { 3, 1 }, false);
+
+ checkCopy(m, null, 1, 0, 2, 4, true);
+ checkCopy(m, null, -1, 1, 2, 2, true);
+ checkCopy(m, null, 1, 0, 2, 2, true);
+ checkCopy(m, null, 1, 0, 2, 4, true);
+ checkCopy(m, null, new int[] {}, new int[] { 0 }, true);
+ checkCopy(m, null, new int[] { 0 }, new int[] { 4 }, true);
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int startRow, int endRow, int startColumn, int endColumn,
+ boolean mustFail) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(startRow, endRow, startColumn, endColumn, sub);
+ assertEquals(new RealMatrixImpl(reference), new RealMatrixImpl(sub));
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ private void checkCopy(RealMatrix m, double[][] reference,
+ int[] selectedRows, int[] selectedColumns,
+ boolean mustFail) {
+ try {
+ double[][] sub = (reference == null) ?
+ new double[1][1] :
+ new double[reference.length][reference[0].length];
+ m.copySubMatrix(selectedRows, selectedColumns, sub);
+ assertEquals(new RealMatrixImpl(reference), new RealMatrixImpl(sub));
+ if (mustFail) {
+ fail("Expecting MatrixIndexException");
+ }
+ } catch (MatrixIndexException e) {
+ if (!mustFail) {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetRowMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealMatrix mRow0 = new RealMatrixImpl(subRow0);
+ RealMatrix mRow3 = new RealMatrixImpl(subRow3);
+ assertEquals("Row0", mRow0,
+ m.getRowMatrix(0));
+ assertEquals("Row3", mRow3,
+ m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealMatrix mRow3 = new RealMatrixImpl(subRow3);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowMatrix(0, mRow3);
+ assertEquals(mRow3, m.getRowMatrix(0));
+ try {
+ m.setRowMatrix(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealMatrix mColumn1 = new RealMatrixImpl(subColumn1);
+ RealMatrix mColumn3 = new RealMatrixImpl(subColumn3);
+ assertEquals("Column1", mColumn1,
+ m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3,
+ m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnMatrix() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealMatrix mColumn3 = new RealMatrixImpl(subColumn3);
+ assertNotSame(mColumn3, m.getColumnMatrix(1));
+ m.setColumnMatrix(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnMatrix(1));
+ try {
+ m.setColumnMatrix(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnMatrix(0, m);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowVector() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealVector mRow0 = new ArrayRealVector(subRow0[0]);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertEquals("Row0", mRow0, m.getRowVector(0));
+ assertEquals("Row3", mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRowVector() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertNotSame(mRow3, m.getRowMatrix(0));
+ m.setRowVector(0, mRow3);
+ assertEquals(mRow3, m.getRowVector(0));
+ try {
+ m.setRowVector(-1, mRow3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRowVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnVector() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealVector mColumn1 = columnToVector(subColumn1);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnVector(1));
+ assertEquals("Column3", mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumnVector() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertNotSame(mColumn3, m.getColumnVector(1));
+ m.setColumnVector(1, mColumn3);
+ assertEquals(mColumn3, m.getColumnVector(1));
+ try {
+ m.setColumnVector(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumnVector(0, new ArrayRealVector(5));
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private RealVector columnToVector(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayRealVector(data, false);
+ }
+
+ public void testGetRow() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ checkArrays(subRow0[0], m.getRow(0));
+ checkArrays(subRow3[0], m.getRow(3));
+ try {
+ m.getRow(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRow(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetRow() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ assertTrue(subRow3[0][0] != m.getRow(0)[0]);
+ m.setRow(0, subRow3[0]);
+ checkArrays(subRow3[0], m.getRow(0));
+ try {
+ m.setRow(-1, subRow3[0]);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setRow(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumn() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ double[] mColumn1 = columnToArray(subColumn1);
+ double[] mColumn3 = columnToArray(subColumn3);
+ checkArrays(mColumn1, m.getColumn(1));
+ checkArrays(mColumn3, m.getColumn(3));
+ try {
+ m.getColumn(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumn(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testSetColumn() {
+ RealMatrix m = new RealMatrixImpl(subTestData);
+ double[] mColumn3 = columnToArray(subColumn3);
+ assertTrue(mColumn3[0] != m.getColumn(1)[0]);
+ m.setColumn(1, mColumn3);
+ checkArrays(mColumn3, m.getColumn(1));
+ try {
+ m.setColumn(-1, mColumn3);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.setColumn(0, new double[5]);
+ fail("Expecting InvalidMatrixException");
+ } catch (InvalidMatrixException ex) {
+ // expected
+ }
+ }
+
+ private double[] columnToArray(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return data;
+ }
+
+ private void checkArrays(double[] expected, double[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testEqualsAndHashCode() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ RealMatrixImpl m1 = (RealMatrixImpl) m.copy();
+ RealMatrixImpl mt = (RealMatrixImpl) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(new RealMatrixImpl(bigSingular)));
+ }
+
+ public void testToString() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ assertEquals("RealMatrixImpl{{1.0,2.0,3.0},{2.0,5.0,3.0},{1.0,0.0,8.0}}",
+ m.toString());
+ m = new RealMatrixImpl();
+ assertEquals("RealMatrixImpl{}",
+ m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ m.setSubMatrix(detData2,1,1);
+ RealMatrix expected = MatrixUtils.createRealMatrix
+ (new double[][] {{1.0,2.0,3.0},{2.0,1.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2,0,0);
+ expected = MatrixUtils.createRealMatrix
+ (new double[][] {{1.0,3.0,3.0},{2.0,4.0,3.0},{1.0,2.0,4.0}});
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2,0,0);
+ expected = MatrixUtils.createRealMatrix
+ (new double[][] {{3.0,4.0,5.0},{4.0,7.0,5.0},{3.0,2.0,10.0}});
+ assertEquals(expected, m);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData,1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData,-1,1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData,1,-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null,1,1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ RealMatrixImpl m2 = new RealMatrixImpl();
+ try {
+ m2.setSubMatrix(testData,0,1);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ try {
+ m2.setSubMatrix(testData,1,0);
+ fail("expecting IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new double[][] {{1}, {2, 3}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new double[][] {{}}, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testWalk() throws MatrixIndexException, MatrixVisitorException {
+ int rows = 150;
+ int columns = 75;
+
+ RealMatrix m = new RealMatrixImpl(rows, columns);
+ m.walkInRowOrder(new SetVisitor());
+ GetVisitor getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInRowOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInColumnOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInColumnOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInOptimizedOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInRowOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor());
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor);
+ assertEquals(rows * columns, getVisitor.getCount());
+
+ m = new RealMatrixImpl(rows, columns);
+ m.walkInOptimizedOrder(new SetVisitor(), 1, rows - 2, 1, columns - 2);
+ getVisitor = new GetVisitor();
+ m.walkInColumnOrder(getVisitor, 1, rows - 2, 1, columns - 2);
+ assertEquals((rows - 2) * (columns - 2), getVisitor.getCount());
+ for (int i = 0; i < rows; ++i) {
+ assertEquals(0.0, m.getEntry(i, 0), 0);
+ assertEquals(0.0, m.getEntry(i, columns - 1), 0);
+ }
+ for (int j = 0; j < columns; ++j) {
+ assertEquals(0.0, m.getEntry(0, j), 0);
+ assertEquals(0.0, m.getEntry(rows - 1, j), 0);
+ }
+
+ }
+
+ public void testSerial() {
+ RealMatrixImpl m = new RealMatrixImpl(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+
+ private static class SetVisitor extends DefaultRealMatrixChangingVisitor {
+ @Override
+ public double visit(int i, int j, double value) {
+ return i + j / 1024.0;
+ }
+ }
+
+ private static class GetVisitor extends DefaultRealMatrixPreservingVisitor {
+ private int count = 0;
+ @Override
+ public void visit(int i, int j, double value) {
+ ++count;
+ assertEquals(i + j / 1024.0, value, 0.0);
+ }
+ public int getCount() {
+ return count;
+ }
+ }
+
+ //--------------- -----------------Protected methods
+
+ /** extracts the l and u matrices from compact lu representation */
+ protected void splitLU(RealMatrix lu, double[][] lowerData, double[][] upperData) throws InvalidMatrixException {
+ if (!lu.isSquare() || lowerData.length != lowerData[0].length || upperData.length != upperData[0].length ||
+ lowerData.length != upperData.length
+ || lowerData.length != lu.getRowDimension()) {
+ throw new InvalidMatrixException("incorrect dimensions");
+ }
+ int n = lu.getRowDimension();
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (j < i) {
+ lowerData[i][j] = lu.getEntry(i, j);
+ upperData[i][j] = 0d;
+ } else if (i == j) {
+ lowerData[i][j] = 1d;
+ upperData[i][j] = lu.getEntry(i, j);
+ } else {
+ lowerData[i][j] = 0d;
+ upperData[i][j] = lu.getEntry(i, j);
+ }
+ }
+ }
+ }
+
+ /** Returns the result of applying the given row permutation to the matrix */
+ protected RealMatrix permuteRows(RealMatrix matrix, int[] permutation) {
+ if (!matrix.isSquare() || matrix.getRowDimension() != permutation.length) {
+ throw new IllegalArgumentException("dimension mismatch");
+ }
+ int n = matrix.getRowDimension();
+ int m = matrix.getColumnDimension();
+ double out[][] = new double[m][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ out[i][j] = matrix.getEntry(permutation[i], j);
+ }
+ }
+ return new RealMatrixImpl(out);
+ }
+
+// /** Useful for debugging */
+// private void dumpMatrix(RealMatrix m) {
+// for (int i = 0; i < m.getRowDimension(); i++) {
+// String os = "";
+// for (int j = 0; j < m.getColumnDimension(); j++) {
+// os += m.getEntry(i, j) + " ";
+// }
+// System.out.println(os);
+// }
+// }
+
+}
+
diff --git a/src/test/java/org/apache/commons/math/linear/RealVectorFormatAbstractTest.java b/src/test/java/org/apache/commons/math/linear/RealVectorFormatAbstractTest.java
new file mode 100644
index 0000000..800098d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/RealVectorFormatAbstractTest.java
@@ -0,0 +1,387 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.util.CompositeFormat;
+
+public abstract class RealVectorFormatAbstractTest extends TestCase {
+
+ RealVectorFormat realVectorFormat = null;
+ RealVectorFormat realVectorFormatSquare = null;
+
+ protected abstract Locale getLocale();
+
+ protected abstract char getDecimalCharacter();
+
+ @Override
+ public void setUp() throws Exception {
+ realVectorFormat = RealVectorFormat.getInstance(getLocale());
+ final NumberFormat nf = NumberFormat.getInstance(getLocale());
+ nf.setMaximumFractionDigits(2);
+ realVectorFormatSquare = new RealVectorFormat("[", "]", " : ", nf);
+ }
+
+ public void testSimpleNoDecimals() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1, 1, 1});
+ String expected = "{1; 1; 1}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimals() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1.23, 1.43, 1.63});
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testSimpleWithDecimalsTrunc() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1.2323, 1.4343, 1.6333});
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeX() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {-1.2323, 1.4343, 1.6333});
+ String expected =
+ "{-1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeY() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1.2323, -1.4343, 1.6333});
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; -1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNegativeZ() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1.2323, 1.4343, -1.6333});
+ String expected =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; -1" + getDecimalCharacter() +
+ "63}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testNonDefaultSetting() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {1, 1, 1});
+ String expected = "[1 : 1 : 1]";
+ String actual = realVectorFormatSquare.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testStaticFormatRealVectorImpl() {
+ Locale defaultLocal = Locale.getDefault();
+ Locale.setDefault(getLocale());
+
+ ArrayRealVector c = new ArrayRealVector(new double[] {232.222, -342.33, 432.444});
+ String expected =
+ "{232" + getDecimalCharacter() +
+ "22; -342" + getDecimalCharacter() +
+ "33; 432" + getDecimalCharacter() +
+ "44}";
+ String actual = RealVectorFormat.formatRealVector(c);
+ assertEquals(expected, actual);
+
+ Locale.setDefault(defaultLocal);
+ }
+
+ public void testNan() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {Double.NaN, Double.NaN, Double.NaN});
+ String expected = "{(NaN); (NaN); (NaN)}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testPositiveInfinity() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {
+ Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY
+ });
+ String expected = "{(Infinity); (Infinity); (Infinity)}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void tesNegativeInfinity() {
+ ArrayRealVector c = new ArrayRealVector(new double[] {
+ Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY
+ });
+ String expected = "{(-Infinity); (-Infinity); (-Infinity)}";
+ String actual = realVectorFormat.format(c);
+ assertEquals(expected, actual);
+ }
+
+ public void testParseSimpleNoDecimals() {
+ String source = "{1; 1; 1}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1, 1, 1});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseIgnoredWhitespace() {
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1, 1, 1});
+ ParsePosition pos1 = new ParsePosition(0);
+ String source1 = "{1;1;1}";
+ assertEquals(expected, realVectorFormat.parseObject(source1, pos1));
+ assertEquals(source1.length(), pos1.getIndex());
+ ParsePosition pos2 = new ParsePosition(0);
+ String source2 = " { 1 ; 1 ; 1 } ";
+ assertEquals(expected, realVectorFormat.parseObject(source2, pos2));
+ assertEquals(source2.length() - 1, pos2.getIndex());
+ }
+
+ public void testParseSimpleWithDecimals() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "23; 1" + getDecimalCharacter() +
+ "43; 1" + getDecimalCharacter() +
+ "63}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1.23, 1.43, 1.63});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseSimpleWithDecimalsTrunc() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1.2323, 1.4343, 1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeX() {
+ String source =
+ "{-1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {-1.2323, 1.4343, 1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeY() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; -1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1.2323, -1.4343, 1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeZ() {
+ String source =
+ "{1" + getDecimalCharacter() +
+ "2323; 1" + getDecimalCharacter() +
+ "4343; -1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1.2323, 1.4343, -1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeAll() {
+ String source =
+ "{-1" + getDecimalCharacter() +
+ "2323; -1" + getDecimalCharacter() +
+ "4343; -1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {-1.2323, -1.4343, -1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseZeroX() {
+ String source =
+ "{0" + getDecimalCharacter() +
+ "0; -1" + getDecimalCharacter() +
+ "4343; 1" + getDecimalCharacter() +
+ "6333}";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {0.0, -1.4343, 1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNonDefaultSetting() {
+ String source =
+ "[1" + getDecimalCharacter() +
+ "2323 : 1" + getDecimalCharacter() +
+ "4343 : 1" + getDecimalCharacter() +
+ "6333]";
+ ArrayRealVector expected = new ArrayRealVector(new double[] {1.2323, 1.4343, 1.6333});
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormatSquare.parseObject(source);
+ assertEquals(expected, actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNan() {
+ String source = "{(NaN); (NaN); (NaN)}";
+ try {
+ ArrayRealVector actual = (ArrayRealVector) realVectorFormat.parseObject(source);
+ assertEquals(new ArrayRealVector(new double[] {Double.NaN, Double.NaN, Double.NaN}), actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParsePositiveInfinity() {
+ String source = "{(Infinity); (Infinity); (Infinity)}";
+ try {
+ ArrayRealVector actual = (ArrayRealVector)realVectorFormat.parseObject(source);
+ assertEquals(new ArrayRealVector(new double[] {
+ Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY
+ }), actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNegativeInfinity() {
+ String source = "{(-Infinity); (-Infinity); (-Infinity)}";
+ try {
+ ArrayRealVector actual = (ArrayRealVector)realVectorFormat.parseObject(source);
+ assertEquals(new ArrayRealVector(new double[] {
+ Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY
+ }), actual);
+ } catch (ParseException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ public void testParseNoComponents() {
+ try {
+ realVectorFormat.parseObject("{ }");
+ fail("Expecting ParseException");
+ } catch (ParseException pe) {
+ // expected behavior
+ }
+ }
+
+ public void testParseManyComponents() throws ParseException {
+ ArrayRealVector parsed =
+ (ArrayRealVector) realVectorFormat.parseObject("{0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0}");
+ assertEquals(24, parsed.getDimension());
+ }
+
+ public void testConstructorSingleFormat() {
+ NumberFormat nf = NumberFormat.getInstance();
+ RealVectorFormat cf = new RealVectorFormat(nf);
+ assertNotNull(cf);
+ assertEquals(nf, cf.getFormat());
+ }
+
+ public void testFormatObject() {
+ try {
+ CompositeFormat cf = new RealVectorFormat();
+ Object object = new Object();
+ cf.format(object);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testForgottenPrefix() {
+ ParsePosition pos = new ParsePosition(0);
+ final String source = "1; 1; 1}";
+ assertNull("Should not parse <"+source+">",new RealVectorFormat().parse(source, pos));
+ assertEquals(0, pos.getErrorIndex());
+ }
+
+ public void testForgottenSeparator() {
+ ParsePosition pos = new ParsePosition(0);
+ final String source = "{1; 1 1}";
+ assertNull("Should not parse <"+source+">",new RealVectorFormat().parse(source, pos));
+ assertEquals(6, pos.getErrorIndex());
+ }
+
+ public void testForgottenSuffix() {
+ ParsePosition pos = new ParsePosition(0);
+ final String source = "{1; 1; 1 ";
+ assertNull("Should not parse <"+source+">",new RealVectorFormat().parse(source, pos));
+ assertEquals(8, pos.getErrorIndex());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/RealVectorFormatTest.java b/src/test/java/org/apache/commons/math/linear/RealVectorFormatTest.java
new file mode 100644
index 0000000..e7368b5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/RealVectorFormatTest.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.linear;
+
+import java.util.Locale;
+
+
+public class RealVectorFormatTest extends RealVectorFormatAbstractTest {
+
+ @Override
+ protected char getDecimalCharacter() {
+ return '.';
+ }
+
+ @Override
+ protected Locale getLocale() {
+ return Locale.US;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java b/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java
new file mode 100644
index 0000000..1de28e2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.linear;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class SingularValueDecompositionImplTest extends TestCase {
+
+ private double[][] testSquare = {
+ { 24.0 / 25.0, 43.0 / 25.0 },
+ { 57.0 / 25.0, 24.0 / 25.0 }
+ };
+
+ private double[][] testNonSquare = {
+ { -540.0 / 625.0, 963.0 / 625.0, -216.0 / 625.0 },
+ { -1730.0 / 625.0, -744.0 / 625.0, 1008.0 / 625.0 },
+ { -720.0 / 625.0, 1284.0 / 625.0, -288.0 / 625.0 },
+ { -360.0 / 625.0, 192.0 / 625.0, 1756.0 / 625.0 },
+ };
+
+ private static final double normTolerance = 10e-14;
+
+ public SingularValueDecompositionImplTest(String name) {
+ super(name);
+ }
+
+ public void testMoreRows() {
+ final double[] singularValues = { 123.456, 2.3, 1.001, 0.999 };
+ final int rows = singularValues.length + 2;
+ final int columns = singularValues.length;
+ Random r = new Random(15338437322523l);
+ SingularValueDecomposition svd =
+ new SingularValueDecompositionImpl(createTestMatrix(r, rows, columns, singularValues));
+ double[] computedSV = svd.getSingularValues();
+ assertEquals(singularValues.length, computedSV.length);
+ for (int i = 0; i < singularValues.length; ++i) {
+ assertEquals(singularValues[i], computedSV[i], 1.0e-10);
+ }
+ }
+
+ public void testMoreColumns() {
+ final double[] singularValues = { 123.456, 2.3, 1.001, 0.999 };
+ final int rows = singularValues.length;
+ final int columns = singularValues.length + 2;
+ Random r = new Random(732763225836210l);
+ SingularValueDecomposition svd =
+ new SingularValueDecompositionImpl(createTestMatrix(r, rows, columns, singularValues));
+ double[] computedSV = svd.getSingularValues();
+ assertEquals(singularValues.length, computedSV.length);
+ for (int i = 0; i < singularValues.length; ++i) {
+ assertEquals(singularValues[i], computedSV[i], 1.0e-10);
+ }
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ RealMatrix matrix = MatrixUtils.createRealMatrix(testSquare);
+ final int m = matrix.getRowDimension();
+ final int n = matrix.getColumnDimension();
+ SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix);
+ assertEquals(m, svd.getU().getRowDimension());
+ assertEquals(m, svd.getU().getColumnDimension());
+ assertEquals(m, svd.getS().getColumnDimension());
+ assertEquals(n, svd.getS().getColumnDimension());
+ assertEquals(n, svd.getV().getRowDimension());
+ assertEquals(n, svd.getV().getColumnDimension());
+
+ }
+
+ /** Test based on a dimension 4 Hadamard matrix. */
+ public void testHadamard() {
+ RealMatrix matrix = new Array2DRowRealMatrix(new double[][] {
+ {15.0 / 2.0, 5.0 / 2.0, 9.0 / 2.0, 3.0 / 2.0 },
+ { 5.0 / 2.0, 15.0 / 2.0, 3.0 / 2.0, 9.0 / 2.0 },
+ { 9.0 / 2.0, 3.0 / 2.0, 15.0 / 2.0, 5.0 / 2.0 },
+ { 3.0 / 2.0, 9.0 / 2.0, 5.0 / 2.0, 15.0 / 2.0 }
+ }, false);
+ SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix);
+ assertEquals(16.0, svd.getSingularValues()[0], 1.0e-14);
+ assertEquals( 8.0, svd.getSingularValues()[1], 1.0e-14);
+ assertEquals( 4.0, svd.getSingularValues()[2], 1.0e-14);
+ assertEquals( 2.0, svd.getSingularValues()[3], 1.0e-14);
+
+ RealMatrix fullCovariance = new Array2DRowRealMatrix(new double[][] {
+ { 85.0 / 1024, -51.0 / 1024, -75.0 / 1024, 45.0 / 1024 },
+ { -51.0 / 1024, 85.0 / 1024, 45.0 / 1024, -75.0 / 1024 },
+ { -75.0 / 1024, 45.0 / 1024, 85.0 / 1024, -51.0 / 1024 },
+ { 45.0 / 1024, -75.0 / 1024, -51.0 / 1024, 85.0 / 1024 }
+ }, false);
+ assertEquals(0.0,
+ fullCovariance.subtract(svd.getCovariance(0.0)).getNorm(),
+ 1.0e-14);
+
+ RealMatrix halfCovariance = new Array2DRowRealMatrix(new double[][] {
+ { 5.0 / 1024, -3.0 / 1024, 5.0 / 1024, -3.0 / 1024 },
+ { -3.0 / 1024, 5.0 / 1024, -3.0 / 1024, 5.0 / 1024 },
+ { 5.0 / 1024, -3.0 / 1024, 5.0 / 1024, -3.0 / 1024 },
+ { -3.0 / 1024, 5.0 / 1024, -3.0 / 1024, 5.0 / 1024 }
+ }, false);
+ assertEquals(0.0,
+ halfCovariance.subtract(svd.getCovariance(6.0)).getNorm(),
+ 1.0e-14);
+
+ }
+
+ /** test A = USVt */
+ public void testAEqualUSVt() {
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testSquare));
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare));
+ checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare).transpose());
+ }
+
+ public void checkAEqualUSVt(final RealMatrix matrix) {
+ SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix);
+ RealMatrix u = svd.getU();
+ RealMatrix s = svd.getS();
+ RealMatrix v = svd.getV();
+ double norm = u.multiply(s).multiply(v.transpose()).subtract(matrix).getNorm();
+ assertEquals(0, norm, normTolerance);
+
+ }
+
+ /** test that U is orthogonal */
+ public void testUOrthogonal() {
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getU());
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare)).getU());
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getU());
+ }
+
+ /** test that V is orthogonal */
+ public void testVOrthogonal() {
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getV());
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare)).getV());
+ checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getV());
+ }
+
+ public void checkOrthogonal(final RealMatrix m) {
+ RealMatrix mTm = m.transpose().multiply(m);
+ RealMatrix id = MatrixUtils.createRealIdentityMatrix(mTm.getRowDimension());
+ assertEquals(0, mTm.subtract(id).getNorm(), normTolerance);
+ }
+
+ /** test matrices values */
+ public void testMatricesValues1() {
+ SingularValueDecomposition svd =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare));
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 3.0 / 5.0, -4.0 / 5.0 },
+ { 4.0 / 5.0, 3.0 / 5.0 }
+ });
+ RealMatrix sRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 3.0, 0.0 },
+ { 0.0, 1.0 }
+ });
+ RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 4.0 / 5.0, 3.0 / 5.0 },
+ { 3.0 / 5.0, -4.0 / 5.0 }
+ });
+
+ // check values against known references
+ RealMatrix u = svd.getU();
+ assertEquals(0, u.subtract(uRef).getNorm(), normTolerance);
+ RealMatrix s = svd.getS();
+ assertEquals(0, s.subtract(sRef).getNorm(), normTolerance);
+ RealMatrix v = svd.getV();
+ assertEquals(0, v.subtract(vRef).getNorm(), normTolerance);
+
+ // check the same cached instance is returned the second time
+ assertTrue(u == svd.getU());
+ assertTrue(s == svd.getS());
+ assertTrue(v == svd.getV());
+
+ }
+
+ /** test matrices values */
+ // This test is useless since whereas the columns of U and V are linked
+ // together, the actual triplet (U,S,V) is not uniquely defined.
+ public void useless_testMatricesValues2() {
+
+ RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 0.0 / 5.0, 3.0 / 5.0, 0.0 / 5.0 },
+ { -4.0 / 5.0, 0.0 / 5.0, -3.0 / 5.0 },
+ { 0.0 / 5.0, 4.0 / 5.0, 0.0 / 5.0 },
+ { -3.0 / 5.0, 0.0 / 5.0, 4.0 / 5.0 }
+ });
+ RealMatrix sRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 4.0, 0.0, 0.0 },
+ { 0.0, 3.0, 0.0 },
+ { 0.0, 0.0, 2.0 }
+ });
+ RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
+ { 80.0 / 125.0, -60.0 / 125.0, 75.0 / 125.0 },
+ { 24.0 / 125.0, 107.0 / 125.0, 60.0 / 125.0 },
+ { -93.0 / 125.0, -24.0 / 125.0, 80.0 / 125.0 }
+ });
+
+ // check values against known references
+ SingularValueDecomposition svd =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare));
+ RealMatrix u = svd.getU();
+ assertEquals(0, u.subtract(uRef).getNorm(), normTolerance);
+ RealMatrix s = svd.getS();
+ assertEquals(0, s.subtract(sRef).getNorm(), normTolerance);
+ RealMatrix v = svd.getV();
+ assertEquals(0, v.subtract(vRef).getNorm(), normTolerance);
+
+ // check the same cached instance is returned the second time
+ assertTrue(u == svd.getU());
+ assertTrue(s == svd.getS());
+ assertTrue(v == svd.getV());
+
+ }
+
+ /** test condition number */
+ public void testConditionNumber() {
+ SingularValueDecompositionImpl svd =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare));
+ // replace 1.0e-15 with 1.5e-15
+ assertEquals(3.0, svd.getConditionNumber(), 1.5e-15);
+ }
+
+ private RealMatrix createTestMatrix(final Random r, final int rows, final int columns,
+ final double[] singularValues) {
+ final RealMatrix u =
+ EigenDecompositionImplTest.createOrthogonalMatrix(r, rows);
+ final RealMatrix d =
+ EigenDecompositionImplTest.createDiagonalMatrix(singularValues, rows, columns);
+ final RealMatrix v =
+ EigenDecompositionImplTest.createOrthogonalMatrix(r, columns);
+ return u.multiply(d).multiply(v);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java b/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java
new file mode 100644
index 0000000..b333bd0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.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;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SingularValueSolverTest {
+
+ private double[][] testSquare = {
+ { 24.0 / 25.0, 43.0 / 25.0 },
+ { 57.0 / 25.0, 24.0 / 25.0 }
+ };
+
+ private static final double normTolerance = 10e-14;
+
+ /** test solve dimension errors */
+ @Test
+ public void testSolveDimensionErrors() {
+ DecompositionSolver solver =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]);
+ try {
+ solver.solve(b);
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(b.getColumn(0));
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ try {
+ solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ Assert.fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected behavior
+ }
+ }
+
+ /** test least square solve */
+ @Test
+ public void testLeastSquareSolve() {
+ RealMatrix m =
+ MatrixUtils.createRealMatrix(new double[][] {
+ { 1.0, 0.0 },
+ { 0.0, 0.0 }
+ });
+ DecompositionSolver solver = new SingularValueDecompositionImpl(m).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { 11, 12 }, { 21, 22 }
+ });
+ RealMatrix xMatrix = solver.solve(b);
+ Assert.assertEquals(11, xMatrix.getEntry(0, 0), 1.0e-15);
+ Assert.assertEquals(12, xMatrix.getEntry(0, 1), 1.0e-15);
+ Assert.assertEquals(0, xMatrix.getEntry(1, 0), 1.0e-15);
+ Assert.assertEquals(0, xMatrix.getEntry(1, 1), 1.0e-15);
+ double[] xCol = solver.solve(b.getColumn(0));
+ Assert.assertEquals(11, xCol[0], 1.0e-15);
+ Assert.assertEquals(0, xCol[1], 1.0e-15);
+ RealVector xColVec = solver.solve(b.getColumnVector(0));
+ Assert.assertEquals(11, xColVec.getEntry(0), 1.0e-15);
+ Assert.assertEquals(0, xColVec.getEntry(1), 1.0e-15);
+ RealVector xColOtherVec = solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
+ Assert.assertEquals(11, xColOtherVec.getEntry(0), 1.0e-15);
+ Assert.assertEquals(0, xColOtherVec.getEntry(1), 1.0e-15);
+ }
+
+ /** test solve */
+ @Test
+ public void testSolve() {
+ DecompositionSolver solver =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver();
+ RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 2, 3 }, { 0, -5, 1 }
+ });
+ RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
+ { -8.0 / 25.0, -263.0 / 75.0, -29.0 / 75.0 },
+ { 19.0 / 25.0, 78.0 / 25.0, 49.0 / 25.0 }
+ });
+
+ // using RealMatrix
+ Assert.assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), normTolerance);
+
+ // using double[]
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ Assert.assertEquals(0,
+ new ArrayRealVector(solver.solve(b.getColumn(i))).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using Array2DRowRealMatrix
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ Assert.assertEquals(0,
+ solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ // using RealMatrix with an alternate implementation
+ for (int i = 0; i < b.getColumnDimension(); ++i) {
+ ArrayRealVectorTest.RealVectorTestImpl v =
+ new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
+ Assert.assertEquals(0,
+ solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
+ 1.0e-13);
+ }
+
+ }
+
+ /** test condition number */
+ @Test
+ public void testConditionNumber() {
+ SingularValueDecompositionImpl svd =
+ new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare));
+ // replace 1.0e-15 with 1.5e-15
+ Assert.assertEquals(3.0, svd.getConditionNumber(), 1.5e-15);
+ }
+
+ @Test
+ public void testMath320B() {
+ RealMatrix rm = new Array2DRowRealMatrix(new double[][] {
+ { 1.0, 2.0 }, { 1.0, 2.0 }
+ });
+ SingularValueDecomposition svd =
+ new SingularValueDecompositionImpl(rm);
+ RealMatrix recomposed = svd.getU().multiply(svd.getS()).multiply(svd.getVT());
+ Assert.assertEquals(0.0, recomposed.subtract(rm).getNorm(), 2.0e-15);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SparseFieldMatrixTest.java b/src/test/java/org/apache/commons/math/linear/SparseFieldMatrixTest.java
new file mode 100644
index 0000000..90a6d4a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SparseFieldMatrixTest.java
@@ -0,0 +1,684 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionConversionException;
+import org.apache.commons.math.fraction.FractionField;
+
+/**
+ * Test cases for the {@link SparseFieldMatrix} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class SparseFieldMatrixTest extends TestCase {
+ // 3 x 3 identity matrix
+ protected Fraction[][] id = { {new Fraction(1), new Fraction(0), new Fraction(0) }, { new Fraction(0), new Fraction(1), new Fraction(0) }, { new Fraction(0), new Fraction(0), new Fraction(1) } };
+ // Test data for group operations
+ protected Fraction[][] testData = { { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(5), new Fraction(3) },
+ { new Fraction(1), new Fraction(0), new Fraction(8) } };
+ protected Fraction[][] testDataLU = null;
+ protected Fraction[][] testDataPlus2 = { { new Fraction(3), new Fraction(4), new Fraction(5) }, { new Fraction(4), new Fraction(7), new Fraction(5) },
+ { new Fraction(3), new Fraction(2), new Fraction(10) } };
+ protected Fraction[][] testDataMinus = { { new Fraction(-1), new Fraction(-2), new Fraction(-3) },
+ { new Fraction(-2), new Fraction(-5), new Fraction(-3) }, { new Fraction(-1), new Fraction(0), new Fraction(-8) } };
+ protected Fraction[] testDataRow1 = { new Fraction(1), new Fraction(2), new Fraction(3) };
+ protected Fraction[] testDataCol3 = { new Fraction(3), new Fraction(3), new Fraction(8) };
+ protected Fraction[][] testDataInv = { { new Fraction(-40), new Fraction(16), new Fraction(9) }, { new Fraction(13), new Fraction(-5), new Fraction(-3) },
+ { new Fraction(5), new Fraction(-2), new Fraction(-1) } };
+ protected Fraction[] preMultTest = { new Fraction(8), new Fraction(12), new Fraction(33) };
+ protected Fraction[][] testData2 = { { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(5), new Fraction(3) } };
+ protected Fraction[][] testData2T = { { new Fraction(1), new Fraction(2) }, { new Fraction(2), new Fraction(5) }, { new Fraction(3), new Fraction(3) } };
+ protected Fraction[][] testDataPlusInv = { { new Fraction(-39), new Fraction(18), new Fraction(12) },
+ { new Fraction(15), new Fraction(0), new Fraction(0) }, { new Fraction(6), new Fraction(-2), new Fraction(7) } };
+
+ // lu decomposition tests
+ protected Fraction[][] luData = { { new Fraction(2), new Fraction(3), new Fraction(3) }, { new Fraction(0), new Fraction(5), new Fraction(7) }, { new Fraction(6), new Fraction(9), new Fraction(8) } };
+ protected Fraction[][] luDataLUDecomposition = null;
+
+ // singular matrices
+ protected Fraction[][] singular = { { new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(3) } };
+ protected Fraction[][] bigSingular = { { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) },
+ { new Fraction(2), new Fraction(5), new Fraction(3), new Fraction(4) }, { new Fraction(7), new Fraction(3), new Fraction(256), new Fraction(1930) }, { new Fraction(3), new Fraction(7), new Fraction(6), new Fraction(8) } }; // 4th
+
+ // row
+ // =
+ // 1st
+ // +
+ // 2nd
+ protected Fraction[][] detData = { { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(4), new Fraction(5), new Fraction(6) },
+ { new Fraction(7), new Fraction(8), new Fraction(10) } };
+ protected Fraction[][] detData2 = { { new Fraction(1), new Fraction(3) }, { new Fraction(2), new Fraction(4) } };
+
+ // vectors
+ protected Fraction[] testVector = { new Fraction(1), new Fraction(2), new Fraction(3) };
+ protected Fraction[] testVector2 = { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) };
+
+ // submatrix accessor tests
+ protected Fraction[][] subTestData = null;
+
+ // array selections
+ protected Fraction[][] subRows02Cols13 = { {new Fraction(2), new Fraction(4) }, { new Fraction(4), new Fraction(8) } };
+ protected Fraction[][] subRows03Cols12 = { { new Fraction(2), new Fraction(3) }, { new Fraction(5), new Fraction(6) } };
+ protected Fraction[][] subRows03Cols123 = { { new Fraction(2), new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6), new Fraction(7) } };
+
+ // effective permutations
+ protected Fraction[][] subRows20Cols123 = { { new Fraction(4), new Fraction(6), new Fraction(8) }, { new Fraction(2), new Fraction(3), new Fraction(4) } };
+ protected Fraction[][] subRows31Cols31 = null;
+
+ // contiguous ranges
+ protected Fraction[][] subRows01Cols23 = null;
+ protected Fraction[][] subRows23Cols00 = { { new Fraction(2) }, { new Fraction(4) } };
+ protected Fraction[][] subRows00Cols33 = { { new Fraction(4) } };
+
+ // row matrices
+ protected Fraction[][] subRow0 = { { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) } };
+ protected Fraction[][] subRow3 = { { new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7) } };
+
+ // column matrices
+ protected Fraction[][] subColumn1 = null;
+ protected Fraction[][] subColumn3 = null;
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+ protected Field<Fraction> field = FractionField.getInstance();
+
+ public SparseFieldMatrixTest(String name) {
+ super(name);
+ setupFractionArrays();
+ }
+
+ private void setupFractionArrays() {
+ try {
+ testDataLU = new Fraction[][]{ { new Fraction(2), new Fraction(5), new Fraction(3) }, { new Fraction(.5d), new Fraction(-2.5d), new Fraction(6.5d) },
+ { new Fraction(0.5d), new Fraction(0.2d), new Fraction(.2d) } };
+ luDataLUDecomposition = new Fraction[][]{ { new Fraction(6), new Fraction(9), new Fraction(8) },
+ { new Fraction(0), new Fraction(5), new Fraction(7) }, { new Fraction(0.33333333333333), new Fraction(0), new Fraction(0.33333333333333) } };
+ subTestData = new Fraction [][]{ { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) },
+ { new Fraction(1.5), new Fraction(2.5), new Fraction(3.5), new Fraction(4.5) }, { new Fraction(2), new Fraction(4), new Fraction(6), new Fraction(8) }, { new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7) } };
+ subRows31Cols31 = new Fraction[][]{ { new Fraction(7), new Fraction(5) }, { new Fraction(4.5), new Fraction(2.5) } };
+ subRows01Cols23 = new Fraction[][]{ { new Fraction(3), new Fraction(4) }, { new Fraction(3.5), new Fraction(4.5) } };
+ subColumn1 = new Fraction [][]{ { new Fraction(2) }, { new Fraction(2.5) }, { new Fraction(4) }, { new Fraction(5) } };
+ subColumn3 = new Fraction[][]{ { new Fraction(4) }, { new Fraction(4.5) }, { new Fraction(8) }, { new Fraction(7) } };
+ } catch (FractionConversionException e) {
+ // ignore, can't happen
+ }
+
+
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> m2 = createSparseMatrix(testData2);
+ assertEquals("testData row dimension", 3, m.getRowDimension());
+ assertEquals("testData column dimension", 3, m.getColumnDimension());
+ assertTrue("testData is square", m.isSquare());
+ assertEquals("testData2 row dimension", m2.getRowDimension(), 2);
+ assertEquals("testData2 column dimension", m2.getColumnDimension(), 3);
+ assertTrue("testData2 is not square", !m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ SparseFieldMatrix<Fraction> m1 = createSparseMatrix(testData);
+ FieldMatrix<Fraction> m2 = m1.copy();
+ assertEquals(m1.getClass(), m2.getClass());
+ assertEquals((m2), m1);
+ SparseFieldMatrix<Fraction> m3 = createSparseMatrix(testData);
+ FieldMatrix<Fraction> m4 = m3.copy();
+ assertEquals(m3.getClass(), m4.getClass());
+ assertEquals((m4), m3);
+ }
+
+ /** test add */
+ public void testAdd() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> mInv = createSparseMatrix(testDataInv);
+ SparseFieldMatrix<Fraction> mDataPlusInv = createSparseMatrix(testDataPlusInv);
+ FieldMatrix<Fraction> mPlusMInv = m.add(mInv);
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ mDataPlusInv.getEntry(row, col).doubleValue(), mPlusMInv.getEntry(row, col).doubleValue(),
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> m2 = createSparseMatrix(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> n = createSparseMatrix(testDataInv);
+ assertClose("m-n = m + -n", m.subtract(n),
+ n.scalarMultiply(new Fraction(-1)).add(m), entryTolerance);
+ try {
+ m.subtract(createSparseMatrix(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> mInv = createSparseMatrix(testDataInv);
+ SparseFieldMatrix<Fraction> identity = createSparseMatrix(id);
+ SparseFieldMatrix<Fraction> m2 = createSparseMatrix(testData2);
+ assertClose("inverse multiply", m.multiply(mInv), identity,
+ entryTolerance);
+ assertClose("inverse multiply", m.multiply(new Array2DRowFieldMatrix<Fraction>(testDataInv)), identity,
+ entryTolerance);
+ assertClose("inverse multiply", mInv.multiply(m), identity,
+ entryTolerance);
+ assertClose("identity multiply", m.multiply(identity), m,
+ entryTolerance);
+ assertClose("identity multiply", identity.multiply(mInv), mInv,
+ entryTolerance);
+ assertClose("identity multiply", m2.multiply(identity), m2,
+ entryTolerance);
+ try {
+ m.multiply(createSparseMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ // Additional Test for Array2DRowRealMatrixTest.testMultiply
+
+ private Fraction[][] d3 = new Fraction[][] { { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6), new Fraction(7), new Fraction(8) } };
+ private Fraction[][] d4 = new Fraction[][] { { new Fraction(1) }, { new Fraction(2) }, { new Fraction(3) }, { new Fraction(4) } };
+ private Fraction[][] d5 = new Fraction[][] { { new Fraction(30) }, { new Fraction(70) } };
+
+ public void testMultiply2() {
+ FieldMatrix<Fraction> m3 = createSparseMatrix(d3);
+ FieldMatrix<Fraction> m4 = createSparseMatrix(d4);
+ FieldMatrix<Fraction> m5 = createSparseMatrix(d5);
+ assertClose("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ FieldMatrix<Fraction> m = createSparseMatrix(id);
+ assertEquals("identity trace", 3d, m.getTrace().doubleValue(), entryTolerance);
+ m = createSparseMatrix(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ FieldMatrix<Fraction> m = createSparseMatrix(testData);
+ assertClose("scalar add", createSparseMatrix(testDataPlus2),
+ m.scalarAdd(new Fraction(2)), entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ FieldMatrix<Fraction> m = createSparseMatrix(id);
+ assertClose("identity operate", testVector, m.operate(testVector),
+ entryTolerance);
+ assertClose("identity operate", testVector, m.operate(
+ new ArrayFieldVector<Fraction>(testVector)).getData(), entryTolerance);
+ m = createSparseMatrix(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ FieldMatrix<Fraction> a = createSparseMatrix(new Fraction[][] {
+ { new Fraction(1), new Fraction(2) }, { new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6) } });
+ Fraction[] b = a.operate(new Fraction[] { new Fraction(1), new Fraction(1) });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals(3.0, b[0].doubleValue(), 1.0e-12);
+ assertEquals(7.0, b[1].doubleValue(), 1.0e-12);
+ assertEquals(11.0, b[2].doubleValue(), 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+
+ FieldMatrix<Fraction> m = createSparseMatrix(testData);
+ FieldMatrix<Fraction> mIT = new FieldLUDecompositionImpl<Fraction>(m).getSolver().getInverse().transpose();
+ FieldMatrix<Fraction> mTI = new FieldLUDecompositionImpl<Fraction>(m.transpose()).getSolver().getInverse();
+ assertClose("inverse-transpose", mIT, mTI, normTolerance);
+ m = createSparseMatrix(testData2);
+ FieldMatrix<Fraction> mt = createSparseMatrix(testData2T);
+ assertClose("transpose",mt,m.transpose(),normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ FieldMatrix<Fraction> m = createSparseMatrix(testData);
+ assertClose("premultiply", m.preMultiply(testVector), preMultTest,
+ normTolerance);
+ assertClose("premultiply", m.preMultiply(
+ new ArrayFieldVector<Fraction>(testVector).getData()), preMultTest, normTolerance);
+ m = createSparseMatrix(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ FieldMatrix<Fraction> m3 = createSparseMatrix(d3);
+ FieldMatrix<Fraction> m4 = createSparseMatrix(d4);
+ FieldMatrix<Fraction> m5 = createSparseMatrix(d5);
+ assertClose("m3*m4=m5", m4.preMultiply(m3), m5, entryTolerance);
+
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> mInv = createSparseMatrix(testDataInv);
+ SparseFieldMatrix<Fraction> identity = createSparseMatrix(id);
+ assertClose("inverse multiply", m.preMultiply(mInv), identity,
+ entryTolerance);
+ assertClose("inverse multiply", mInv.preMultiply(m), identity,
+ entryTolerance);
+ assertClose("identity multiply", m.preMultiply(identity), m,
+ entryTolerance);
+ assertClose("identity multiply", identity.preMultiply(mInv), mInv,
+ entryTolerance);
+ try {
+ m.preMultiply(createSparseMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ FieldMatrix<Fraction> m = createSparseMatrix(testData);
+ assertClose("get row", m.getRow(0), testDataRow1, entryTolerance);
+ assertClose("get col", m.getColumn(2), testDataCol3, entryTolerance);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ FieldMatrix<Fraction> m = createSparseMatrix(testData);
+ assertEquals("get entry", m.getEntry(0, 1).doubleValue(), 2d, entryTolerance);
+ try {
+ m.getEntry(10, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ Fraction[][] matrixData = { { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(5), new Fraction(3) } };
+ FieldMatrix<Fraction> m = createSparseMatrix(matrixData);
+ // One more with three rows, two columns
+ Fraction[][] matrixData2 = { { new Fraction(1), new Fraction(2) }, { new Fraction(2), new Fraction(5) }, { new Fraction(1), new Fraction(7) } };
+ FieldMatrix<Fraction> n = createSparseMatrix(matrixData2);
+ // Now multiply m by n
+ FieldMatrix<Fraction> p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ FieldMatrix<Fraction> pInverse = new FieldLUDecompositionImpl<Fraction>(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ Fraction[][] coefficientsData = { { new Fraction(2), new Fraction(3), new Fraction(-2) }, { new Fraction(-1), new Fraction(7), new Fraction(6) },
+ { new Fraction(4), new Fraction(-3), new Fraction(-5) } };
+ FieldMatrix<Fraction> coefficients = createSparseMatrix(coefficientsData);
+ Fraction[] constants = { new Fraction(1), new Fraction(-2), new Fraction(1) };
+ Fraction[] solution = new FieldLUDecompositionImpl<Fraction>(coefficients).getSolver().solve(constants);
+ assertEquals((new Fraction(2).multiply((solution[0])).add(new Fraction(3).multiply(solution[1])).subtract(new Fraction(2).multiply(solution[2]))).doubleValue(),
+ constants[0].doubleValue(), 1E-12);
+ assertEquals(((new Fraction(-1).multiply(solution[0])).add(new Fraction(7).multiply(solution[1])).add(new Fraction(6).multiply(solution[2]))).doubleValue(),
+ constants[1].doubleValue(), 1E-12);
+ assertEquals(((new Fraction(4).multiply(solution[0])).subtract(new Fraction(3).multiply( solution[1])).subtract(new Fraction(5).multiply(solution[2]))).doubleValue(),
+ constants[2].doubleValue(), 1E-12);
+
+ }
+
+ // test submatrix accessors
+ public void testSubMatrix() {
+ FieldMatrix<Fraction> m = createSparseMatrix(subTestData);
+ FieldMatrix<Fraction> mRows23Cols00 = createSparseMatrix(subRows23Cols00);
+ FieldMatrix<Fraction> mRows00Cols33 = createSparseMatrix(subRows00Cols33);
+ FieldMatrix<Fraction> mRows01Cols23 = createSparseMatrix(subRows01Cols23);
+ FieldMatrix<Fraction> mRows02Cols13 = createSparseMatrix(subRows02Cols13);
+ FieldMatrix<Fraction> mRows03Cols12 = createSparseMatrix(subRows03Cols12);
+ FieldMatrix<Fraction> mRows03Cols123 = createSparseMatrix(subRows03Cols123);
+ FieldMatrix<Fraction> mRows20Cols123 = createSparseMatrix(subRows20Cols123);
+ FieldMatrix<Fraction> mRows31Cols31 = createSparseMatrix(subRows31Cols31);
+ assertEquals("Rows23Cols00", mRows23Cols00, m.getSubMatrix(2, 3, 0, 0));
+ assertEquals("Rows00Cols33", mRows00Cols33, m.getSubMatrix(0, 0, 3, 3));
+ assertEquals("Rows01Cols23", mRows01Cols23, m.getSubMatrix(0, 1, 2, 3));
+ assertEquals("Rows02Cols13", mRows02Cols13,
+ m.getSubMatrix(new int[] { 0, 2 }, new int[] { 1, 3 }));
+ assertEquals("Rows03Cols12", mRows03Cols12,
+ m.getSubMatrix(new int[] { 0, 3 }, new int[] { 1, 2 }));
+ assertEquals("Rows03Cols123", mRows03Cols123,
+ m.getSubMatrix(new int[] { 0, 3 }, new int[] { 1, 2, 3 }));
+ assertEquals("Rows20Cols123", mRows20Cols123,
+ m.getSubMatrix(new int[] { 2, 0 }, new int[] { 1, 2, 3 }));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] { 3, 1 }, new int[] { 3, 1 }));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] { 3, 1 }, new int[] { 3, 1 }));
+
+ try {
+ m.getSubMatrix(1, 0, 2, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(-1, 1, 2, 2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1, 0, 2, 2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1, 0, 2, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] {}, new int[] { 0 });
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] { 0 }, new int[] { 4 });
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowMatrix() {
+ FieldMatrix<Fraction> m = createSparseMatrix(subTestData);
+ FieldMatrix<Fraction> mRow0 = createSparseMatrix(subRow0);
+ FieldMatrix<Fraction> mRow3 = createSparseMatrix(subRow3);
+ assertEquals("Row0", mRow0, m.getRowMatrix(0));
+ assertEquals("Row3", mRow3, m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ FieldMatrix<Fraction> m = createSparseMatrix(subTestData);
+ FieldMatrix<Fraction> mColumn1 = createSparseMatrix(subColumn1);
+ FieldMatrix<Fraction> mColumn3 = createSparseMatrix(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3, m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowVector() {
+ FieldMatrix<Fraction> m = createSparseMatrix(subTestData);
+ FieldVector<Fraction> mRow0 = new ArrayFieldVector<Fraction>(subRow0[0]);
+ FieldVector<Fraction> mRow3 = new ArrayFieldVector<Fraction>(subRow3[0]);
+ assertEquals("Row0", mRow0, m.getRowVector(0));
+ assertEquals("Row3", mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnVector() {
+ FieldMatrix<Fraction> m = createSparseMatrix(subTestData);
+ FieldVector<Fraction> mColumn1 = columnToVector(subColumn1);
+ FieldVector<Fraction> mColumn3 = columnToVector(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnVector(1));
+ assertEquals("Column3", mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ private FieldVector<Fraction> columnToVector(Fraction[][] column) {
+ Fraction[] data = new Fraction[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayFieldVector<Fraction>(data, false);
+ }
+
+ public void testEqualsAndHashCode() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ SparseFieldMatrix<Fraction> m1 = (SparseFieldMatrix<Fraction>) m.copy();
+ SparseFieldMatrix<Fraction> mt = (SparseFieldMatrix<Fraction>) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(createSparseMatrix(bigSingular)));
+ }
+
+ /* Disable for now
+ public void testToString() {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ assertEquals("SparseFieldMatrix<Fraction>{{1.0,2.0,3.0},{2.0,5.0,3.0},{1.0,0.0,8.0}}",
+ m.toString());
+ m = new SparseFieldMatrix<Fraction>(field, 1, 1);
+ assertEquals("SparseFieldMatrix<Fraction>{{0.0}}", m.toString());
+ }
+ */
+
+ public void testSetSubMatrix() throws Exception {
+ SparseFieldMatrix<Fraction> m = createSparseMatrix(testData);
+ m.setSubMatrix(detData2, 1, 1);
+ FieldMatrix<Fraction> expected = createSparseMatrix(new Fraction[][] {
+ { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(1), new Fraction(3) }, { new Fraction(1), new Fraction(2), new Fraction(4) } });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2, 0, 0);
+ expected = createSparseMatrix(new Fraction[][] {
+ { new Fraction(1), new Fraction(3), new Fraction(3) }, { new Fraction(2), new Fraction(4), new Fraction(3) }, { new Fraction(1), new Fraction(2), new Fraction(4) } });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2, 0, 0);
+ expected = createSparseMatrix(new Fraction[][] {
+ { new Fraction(3), new Fraction(4), new Fraction(5) }, { new Fraction(4), new Fraction(7), new Fraction(5) }, { new Fraction(3), new Fraction(2), new Fraction(10) } });
+ assertEquals(expected, m);
+
+ // javadoc example
+ SparseFieldMatrix<Fraction> matrix =
+ createSparseMatrix(new Fraction[][] {
+ { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6), new Fraction(7), new Fraction(8) }, { new Fraction(9), new Fraction(0), new Fraction(1), new Fraction(2) } });
+ matrix.setSubMatrix(new Fraction[][] { { new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(6) } }, 1, 1);
+ expected = createSparseMatrix(new Fraction[][] {
+ { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) }, { new Fraction(5), new Fraction(3), new Fraction(4), new Fraction(8) }, { new Fraction(9), new Fraction(5), new Fraction(6), new Fraction(2) } });
+ assertEquals(expected, matrix);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData, 1, 1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData, -1, 1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData, 1, -1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null, 1, 1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new SparseFieldMatrix<Fraction>(field, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new Fraction[][] { { new Fraction(1) }, { new Fraction(2), new Fraction(3) } }, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new Fraction[][] { {} }, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ // --------------- -----------------Protected methods
+
+ /** verifies that two matrices are close (1-norm) */
+ protected void assertClose(String msg, FieldMatrix<Fraction> m, FieldMatrix<Fraction> n,
+ double tolerance) {
+ for(int i=0; i < m.getRowDimension(); i++){
+ for(int j=0; j < m.getColumnDimension(); j++){
+ assertEquals(msg, m.getEntry(i,j).doubleValue(), n.getEntry(i,j).doubleValue(), tolerance);
+ }
+
+ }
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, Fraction[] m, Fraction[] n,
+ double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors not same length");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i].doubleValue(), n[i].doubleValue(),
+ tolerance);
+ }
+ }
+
+ private SparseFieldMatrix<Fraction> createSparseMatrix(Fraction[][] data) {
+ SparseFieldMatrix<Fraction> matrix = new SparseFieldMatrix<Fraction>(field, data.length, data[0].length);
+ for (int row = 0; row < data.length; row++) {
+ for (int col = 0; col < data[row].length; col++) {
+ matrix.setEntry(row, col, data[row][col]);
+ }
+ }
+ return matrix;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SparseFieldVectorTest.java b/src/test/java/org/apache/commons/math/linear/SparseFieldVectorTest.java
new file mode 100644
index 0000000..9e63bf3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SparseFieldVectorTest.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.linear;
+
+
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionConversionException;
+import org.apache.commons.math.fraction.FractionField;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the {@link SparseFieldVector} class.
+ *
+ * @version $Revision: 1003907 $ $Date: 2010-10-03 00:23:34 +0200 (dim. 03 oct. 2010) $
+ */
+public class SparseFieldVectorTest extends TestCase {
+
+ //
+ protected Fraction[][] ma1 = {{new Fraction(1), new Fraction(2), new Fraction(3)}, {new Fraction(4), new Fraction(5), new Fraction(6)}, {new Fraction(7), new Fraction(8), new Fraction(9)}};
+ protected Fraction[] vec1 = {new Fraction(1), new Fraction(2), new Fraction(3)};
+ protected Fraction[] vec2 = {new Fraction(4), new Fraction(5), new Fraction(6)};
+ protected Fraction[] vec3 = {new Fraction(7), new Fraction(8), new Fraction(9)};
+ protected Fraction[] vec4 = {new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7), new Fraction(8), new Fraction(9)};
+ protected Fraction[] vec_null = {new Fraction(0), new Fraction(0), new Fraction(0)};
+ protected Fraction[] dvec1 = {new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4), new Fraction(5), new Fraction(6), new Fraction(7), new Fraction(8),new Fraction(9)};
+ protected Fraction[][] mat1 = {{new Fraction(1), new Fraction(2), new Fraction(3)}, {new Fraction(4), new Fraction(5), new Fraction(6)},{ new Fraction(7), new Fraction(8), new Fraction(9)}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ protected FractionField field = FractionField.getInstance();
+
+ public void testMapFunctions() throws FractionConversionException {
+ SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field,vec1);
+
+ //octave = v1 .+ 2.0
+ FieldVector<Fraction> v_mapAdd = v1.mapAdd(new Fraction(2));
+ Fraction[] result_mapAdd = {new Fraction(3), new Fraction(4), new Fraction(5)};
+ assertEquals("compare vectors" ,result_mapAdd,v_mapAdd.getData());
+
+ //octave = v1 .+ 2.0
+ FieldVector<Fraction> v_mapAddToSelf = v1.copy();
+ v_mapAddToSelf.mapAddToSelf(new Fraction(2));
+ Fraction[] result_mapAddToSelf = {new Fraction(3), new Fraction(4), new Fraction(5)};
+ assertEquals("compare vectors" ,result_mapAddToSelf,v_mapAddToSelf.getData());
+
+ //octave = v1 .- 2.0
+ FieldVector<Fraction> v_mapSubtract = v1.mapSubtract(new Fraction(2));
+ Fraction[] result_mapSubtract = {new Fraction(-1), new Fraction(0), new Fraction(1)};
+ assertEquals("compare vectors" ,result_mapSubtract,v_mapSubtract.getData());
+
+ //octave = v1 .- 2.0
+ FieldVector<Fraction> v_mapSubtractToSelf = v1.copy();
+ v_mapSubtractToSelf.mapSubtractToSelf(new Fraction(2));
+ Fraction[] result_mapSubtractToSelf = {new Fraction(-1), new Fraction(0), new Fraction(1)};
+ assertEquals("compare vectors" ,result_mapSubtractToSelf,v_mapSubtractToSelf.getData());
+
+ //octave = v1 .* 2.0
+ FieldVector<Fraction> v_mapMultiply = v1.mapMultiply(new Fraction(2));
+ Fraction[] result_mapMultiply = {new Fraction(2), new Fraction(4), new Fraction(6)};
+ assertEquals("compare vectors" ,result_mapMultiply,v_mapMultiply.getData());
+
+ //octave = v1 .* 2.0
+ FieldVector<Fraction> v_mapMultiplyToSelf = v1.copy();
+ v_mapMultiplyToSelf.mapMultiplyToSelf(new Fraction(2));
+ Fraction[] result_mapMultiplyToSelf = {new Fraction(2), new Fraction(4), new Fraction(6)};
+ assertEquals("compare vectors" ,result_mapMultiplyToSelf,v_mapMultiplyToSelf.getData());
+
+ //octave = v1 ./ 2.0
+ FieldVector<Fraction> v_mapDivide = v1.mapDivide(new Fraction(2));
+ Fraction[] result_mapDivide = {new Fraction(.5d), new Fraction(1), new Fraction(1.5d)};
+ assertEquals("compare vectors" ,result_mapDivide,v_mapDivide.getData());
+
+ //octave = v1 ./ 2.0
+ FieldVector<Fraction> v_mapDivideToSelf = v1.copy();
+ v_mapDivideToSelf.mapDivideToSelf(new Fraction(2));
+ Fraction[] result_mapDivideToSelf = {new Fraction(.5d), new Fraction(1), new Fraction(1.5d)};
+ assertEquals("compare vectors" ,result_mapDivideToSelf,v_mapDivideToSelf.getData());
+
+ //octave = v1 .^-1
+ FieldVector<Fraction> v_mapInv = v1.mapInv();
+ Fraction[] result_mapInv = {new Fraction(1),new Fraction(0.5d),new Fraction(3.333333333333333e-01d)};
+ assertEquals("compare vectors" ,result_mapInv,v_mapInv.getData());
+
+ //octave = v1 .^-1
+ FieldVector<Fraction> v_mapInvToSelf = v1.copy();
+ v_mapInvToSelf.mapInvToSelf();
+ Fraction[] result_mapInvToSelf = {new Fraction(1),new Fraction(0.5d),new Fraction(3.333333333333333e-01d)};
+ assertEquals("compare vectors" ,result_mapInvToSelf,v_mapInvToSelf.getData());
+
+
+ }
+
+ public void testBasicFunctions() throws FractionConversionException {
+ SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field,vec1);
+ SparseFieldVector<Fraction> v2 = new SparseFieldVector<Fraction>(field,vec2);
+
+ SparseFieldVector<Fraction> v2_t = new SparseFieldVector<Fraction>(field,vec2);
+
+ //octave = v1 + v2
+ FieldVector<Fraction> v_add = v1.add(v2);
+ Fraction[] result_add = {new Fraction(5), new Fraction(7), new Fraction(9)};
+ assertEquals("compare vect" ,v_add.getData(),result_add);
+
+ SparseFieldVector<Fraction> vt2 = new SparseFieldVector<Fraction>(field,vec2);
+ FieldVector<Fraction> v_add_i = v1.add(vt2);
+ Fraction[] result_add_i = {new Fraction(5), new Fraction(7), new Fraction(9)};
+ assertEquals("compare vect" ,v_add_i.getData(),result_add_i);
+
+ //octave = v1 - v2
+ SparseFieldVector<Fraction> v_subtract = v1.subtract(v2);
+ Fraction[] result_subtract = {new Fraction(-3), new Fraction(-3), new Fraction(-3)};
+ assertClose("compare vect" ,v_subtract.getData(),result_subtract,normTolerance);
+
+ FieldVector<Fraction> v_subtract_i = v1.subtract(vt2);
+ Fraction[] result_subtract_i = {new Fraction(-3), new Fraction(-3), new Fraction(-3)};
+ assertClose("compare vect" ,v_subtract_i.getData(),result_subtract_i,normTolerance);
+
+ // octave v1 .* v2
+ FieldVector<Fraction> v_ebeMultiply = v1.ebeMultiply(v2);
+ Fraction[] result_ebeMultiply = {new Fraction(4), new Fraction(10), new Fraction(18)};
+ assertClose("compare vect" ,v_ebeMultiply.getData(),result_ebeMultiply,normTolerance);
+
+ FieldVector<Fraction> v_ebeMultiply_2 = v1.ebeMultiply(v2_t);
+ Fraction[] result_ebeMultiply_2 = {new Fraction(4), new Fraction(10), new Fraction(18)};
+ assertClose("compare vect" ,v_ebeMultiply_2.getData(),result_ebeMultiply_2,normTolerance);
+
+ // octave v1 ./ v2
+ FieldVector<Fraction> v_ebeDivide = v1.ebeDivide(v2);
+ Fraction[] result_ebeDivide = {new Fraction(0.25d), new Fraction(0.4d), new Fraction(0.5d)};
+ assertClose("compare vect" ,v_ebeDivide.getData(),result_ebeDivide,normTolerance);
+
+ FieldVector<Fraction> v_ebeDivide_2 = v1.ebeDivide(v2_t);
+ Fraction[] result_ebeDivide_2 = {new Fraction(0.25d), new Fraction(0.4d), new Fraction(0.5d)};
+ assertClose("compare vect" ,v_ebeDivide_2.getData(),result_ebeDivide_2,normTolerance);
+
+ // octave dot(v1,v2)
+ Fraction dot = v1.dotProduct(v2);
+ assertEquals("compare val ",new Fraction(32), dot);
+
+ // octave dot(v1,v2_t)
+ Fraction dot_2 = v1.dotProduct(v2_t);
+ assertEquals("compare val ",new Fraction(32), dot_2);
+
+ FieldMatrix<Fraction> m_outerProduct = v1.outerProduct(v2);
+ assertEquals("compare val ",new Fraction(4), m_outerProduct.getEntry(0,0));
+
+ FieldMatrix<Fraction> m_outerProduct_2 = v1.outerProduct(v2_t);
+ assertEquals("compare val ",new Fraction(4), m_outerProduct_2.getEntry(0,0));
+
+ }
+
+
+ public void testMisc() {
+ SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field,vec1);
+
+ String out1 = v1.toString();
+ assertTrue("some output ", out1.length()!=0);
+ try {
+ v1.checkVectorDimensions(2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+
+ }
+
+ public void testPredicates() {
+
+ SparseFieldVector<Fraction> v = new SparseFieldVector<Fraction>(field, new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2) });
+
+ v.setEntry(0, field.getZero());
+ assertEquals(v, new SparseFieldVector<Fraction>(field, new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2) }));
+ assertNotSame(v, new SparseFieldVector<Fraction>(field, new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2), new Fraction(3) }));
+
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertEquals(String msg, Fraction[] m, Fraction[] n) {
+ if (m.length != n.length) {
+ fail("vectors have different lengths");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i],n[i]);
+ }
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, Fraction[] m, Fraction[] n, double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors have different lengths");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i].doubleValue(),n[i].doubleValue(), tolerance);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SparseRealMatrixTest.java b/src/test/java/org/apache/commons/math/linear/SparseRealMatrixTest.java
new file mode 100644
index 0000000..ec4fa40
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SparseRealMatrixTest.java
@@ -0,0 +1,671 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+
+/**
+ * Test cases for the {@link OpenMapRealMatrix} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2008-11-07 06:48:13 -0800 (Fri, 07 Nov
+ * 2008) $
+ */
+public final class SparseRealMatrixTest extends TestCase {
+
+ // 3 x 3 identity matrix
+ protected double[][] id = { { 1d, 0d, 0d }, { 0d, 1d, 0d }, { 0d, 0d, 1d } };
+ // Test data for group operations
+ protected double[][] testData = { { 1d, 2d, 3d }, { 2d, 5d, 3d },
+ { 1d, 0d, 8d } };
+ protected double[][] testDataLU = { { 2d, 5d, 3d }, { .5d, -2.5d, 6.5d },
+ { 0.5d, 0.2d, .2d } };
+ protected double[][] testDataPlus2 = { { 3d, 4d, 5d }, { 4d, 7d, 5d },
+ { 3d, 2d, 10d } };
+ protected double[][] testDataMinus = { { -1d, -2d, -3d },
+ { -2d, -5d, -3d }, { -1d, 0d, -8d } };
+ protected double[] testDataRow1 = { 1d, 2d, 3d };
+ protected double[] testDataCol3 = { 3d, 3d, 8d };
+ protected double[][] testDataInv = { { -40d, 16d, 9d }, { 13d, -5d, -3d },
+ { 5d, -2d, -1d } };
+ protected double[] preMultTest = { 8, 12, 33 };
+ protected double[][] testData2 = { { 1d, 2d, 3d }, { 2d, 5d, 3d } };
+ protected double[][] testData2T = { { 1d, 2d }, { 2d, 5d }, { 3d, 3d } };
+ protected double[][] testDataPlusInv = { { -39d, 18d, 12d },
+ { 15d, 0d, 0d }, { 6d, -2d, 7d } };
+
+ // lu decomposition tests
+ protected double[][] luData = { { 2d, 3d, 3d }, { 0d, 5d, 7d }, { 6d, 9d, 8d } };
+ protected double[][] luDataLUDecomposition = { { 6d, 9d, 8d },
+ { 0d, 5d, 7d }, { 0.33333333333333, 0d, 0.33333333333333 } };
+
+ // singular matrices
+ protected double[][] singular = { { 2d, 3d }, { 2d, 3d } };
+ protected double[][] bigSingular = { { 1d, 2d, 3d, 4d },
+ { 2d, 5d, 3d, 4d }, { 7d, 3d, 256d, 1930d }, { 3d, 7d, 6d, 8d } }; // 4th
+
+ // row
+ // =
+ // 1st
+ // +
+ // 2nd
+ protected double[][] detData = { { 1d, 2d, 3d }, { 4d, 5d, 6d },
+ { 7d, 8d, 10d } };
+ protected double[][] detData2 = { { 1d, 3d }, { 2d, 4d } };
+
+ // vectors
+ protected double[] testVector = { 1, 2, 3 };
+ protected double[] testVector2 = { 1, 2, 3, 4 };
+
+ // submatrix accessor tests
+ protected double[][] subTestData = { { 1, 2, 3, 4 },
+ { 1.5, 2.5, 3.5, 4.5 }, { 2, 4, 6, 8 }, { 4, 5, 6, 7 } };
+
+ // array selections
+ protected double[][] subRows02Cols13 = { { 2, 4 }, { 4, 8 } };
+ protected double[][] subRows03Cols12 = { { 2, 3 }, { 5, 6 } };
+ protected double[][] subRows03Cols123 = { { 2, 3, 4 }, { 5, 6, 7 } };
+
+ // effective permutations
+ protected double[][] subRows20Cols123 = { { 4, 6, 8 }, { 2, 3, 4 } };
+ protected double[][] subRows31Cols31 = { { 7, 5 }, { 4.5, 2.5 } };
+
+ // contiguous ranges
+ protected double[][] subRows01Cols23 = { { 3, 4 }, { 3.5, 4.5 } };
+ protected double[][] subRows23Cols00 = { { 2 }, { 4 } };
+ protected double[][] subRows00Cols33 = { { 4 } };
+
+ // row matrices
+ protected double[][] subRow0 = { { 1, 2, 3, 4 } };
+ protected double[][] subRow3 = { { 4, 5, 6, 7 } };
+
+ // column matrices
+ protected double[][] subColumn1 = { { 2 }, { 2.5 }, { 4 }, { 5 } };
+ protected double[][] subColumn3 = { { 4 }, { 4.5 }, { 8 }, { 7 } };
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ public SparseRealMatrixTest(String name) {
+ super(name);
+ }
+
+ /** test dimensions */
+ public void testDimensions() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix m2 = createSparseMatrix(testData2);
+ assertEquals("testData row dimension", 3, m.getRowDimension());
+ assertEquals("testData column dimension", 3, m.getColumnDimension());
+ assertTrue("testData is square", m.isSquare());
+ assertEquals("testData2 row dimension", m2.getRowDimension(), 2);
+ assertEquals("testData2 column dimension", m2.getColumnDimension(), 3);
+ assertTrue("testData2 is not square", !m2.isSquare());
+ }
+
+ /** test copy functions */
+ public void testCopyFunctions() {
+ OpenMapRealMatrix m1 = createSparseMatrix(testData);
+ RealMatrix m2 = m1.copy();
+ assertEquals(m1.getClass(), m2.getClass());
+ assertEquals((m2), m1);
+ OpenMapRealMatrix m3 = createSparseMatrix(testData);
+ RealMatrix m4 = m3.copy();
+ assertEquals(m3.getClass(), m4.getClass());
+ assertEquals((m4), m3);
+ }
+
+ /** test add */
+ public void testAdd() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix mInv = createSparseMatrix(testDataInv);
+ OpenMapRealMatrix mDataPlusInv = createSparseMatrix(testDataPlusInv);
+ RealMatrix mPlusMInv = m.add(mInv);
+ for (int row = 0; row < m.getRowDimension(); row++) {
+ for (int col = 0; col < m.getColumnDimension(); col++) {
+ assertEquals("sum entry entry",
+ mDataPlusInv.getEntry(row, col), mPlusMInv.getEntry(row, col),
+ entryTolerance);
+ }
+ }
+ }
+
+ /** test add failure */
+ public void testAddFail() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix m2 = createSparseMatrix(testData2);
+ try {
+ m.add(m2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test norm */
+ public void testNorm() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix m2 = createSparseMatrix(testData2);
+ assertEquals("testData norm", 14d, m.getNorm(), entryTolerance);
+ assertEquals("testData2 norm", 7d, m2.getNorm(), entryTolerance);
+ }
+
+ /** test m-n = m + -n */
+ public void testPlusMinus() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix n = createSparseMatrix(testDataInv);
+ assertClose("m-n = m + -n", m.subtract(n),
+ n.scalarMultiply(-1d).add(m), entryTolerance);
+ try {
+ m.subtract(createSparseMatrix(testData2));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test multiply */
+ public void testMultiply() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix mInv = createSparseMatrix(testDataInv);
+ OpenMapRealMatrix identity = createSparseMatrix(id);
+ OpenMapRealMatrix m2 = createSparseMatrix(testData2);
+ assertClose("inverse multiply", m.multiply(mInv), identity,
+ entryTolerance);
+ assertClose("inverse multiply", m.multiply(new BlockRealMatrix(testDataInv)), identity,
+ entryTolerance);
+ assertClose("inverse multiply", mInv.multiply(m), identity,
+ entryTolerance);
+ assertClose("identity multiply", m.multiply(identity), m,
+ entryTolerance);
+ assertClose("identity multiply", identity.multiply(mInv), mInv,
+ entryTolerance);
+ assertClose("identity multiply", m2.multiply(identity), m2,
+ entryTolerance);
+ try {
+ m.multiply(createSparseMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ // Additional Test for Array2DRowRealMatrixTest.testMultiply
+
+ private double[][] d3 = new double[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } };
+ private double[][] d4 = new double[][] { { 1 }, { 2 }, { 3 }, { 4 } };
+ private double[][] d5 = new double[][] { { 30 }, { 70 } };
+
+ public void testMultiply2() {
+ RealMatrix m3 = createSparseMatrix(d3);
+ RealMatrix m4 = createSparseMatrix(d4);
+ RealMatrix m5 = createSparseMatrix(d5);
+ assertClose("m3*m4=m5", m3.multiply(m4), m5, entryTolerance);
+ }
+
+ /** test trace */
+ public void testTrace() {
+ RealMatrix m = createSparseMatrix(id);
+ assertEquals("identity trace", 3d, m.getTrace(), entryTolerance);
+ m = createSparseMatrix(testData2);
+ try {
+ m.getTrace();
+ fail("Expecting NonSquareMatrixException");
+ } catch (NonSquareMatrixException ex) {
+ // ignored
+ }
+ }
+
+ /** test sclarAdd */
+ public void testScalarAdd() {
+ RealMatrix m = createSparseMatrix(testData);
+ assertClose("scalar add", createSparseMatrix(testDataPlus2),
+ m.scalarAdd(2d), entryTolerance);
+ }
+
+ /** test operate */
+ public void testOperate() {
+ RealMatrix m = createSparseMatrix(id);
+ assertClose("identity operate", testVector, m.operate(testVector),
+ entryTolerance);
+ assertClose("identity operate", testVector, m.operate(
+ new ArrayRealVector(testVector)).getData(), entryTolerance);
+ m = createSparseMatrix(bigSingular);
+ try {
+ m.operate(testVector);
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ /** test issue MATH-209 */
+ public void testMath209() {
+ RealMatrix a = createSparseMatrix(new double[][] {
+ { 1, 2 }, { 3, 4 }, { 5, 6 } });
+ double[] b = a.operate(new double[] { 1, 1 });
+ assertEquals(a.getRowDimension(), b.length);
+ assertEquals(3.0, b[0], 1.0e-12);
+ assertEquals(7.0, b[1], 1.0e-12);
+ assertEquals(11.0, b[2], 1.0e-12);
+ }
+
+ /** test transpose */
+ public void testTranspose() {
+
+ RealMatrix m = createSparseMatrix(testData);
+ RealMatrix mIT = new LUDecompositionImpl(m).getSolver().getInverse().transpose();
+ RealMatrix mTI = new LUDecompositionImpl(m.transpose()).getSolver().getInverse();
+ assertClose("inverse-transpose", mIT, mTI, normTolerance);
+ m = createSparseMatrix(testData2);
+ RealMatrix mt = createSparseMatrix(testData2T);
+ assertClose("transpose",mt,m.transpose(),normTolerance);
+ }
+
+ /** test preMultiply by vector */
+ public void testPremultiplyVector() {
+ RealMatrix m = createSparseMatrix(testData);
+ assertClose("premultiply", m.preMultiply(testVector), preMultTest,
+ normTolerance);
+ assertClose("premultiply", m.preMultiply(
+ new ArrayRealVector(testVector).getData()), preMultTest, normTolerance);
+ m = createSparseMatrix(bigSingular);
+ try {
+ m.preMultiply(testVector);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testPremultiply() {
+ RealMatrix m3 = createSparseMatrix(d3);
+ RealMatrix m4 = createSparseMatrix(d4);
+ RealMatrix m5 = createSparseMatrix(d5);
+ assertClose("m3*m4=m5", m4.preMultiply(m3), m5, entryTolerance);
+
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix mInv = createSparseMatrix(testDataInv);
+ OpenMapRealMatrix identity = createSparseMatrix(id);
+ assertClose("inverse multiply", m.preMultiply(mInv), identity,
+ entryTolerance);
+ assertClose("inverse multiply", mInv.preMultiply(m), identity,
+ entryTolerance);
+ assertClose("identity multiply", m.preMultiply(identity), m,
+ entryTolerance);
+ assertClose("identity multiply", identity.preMultiply(mInv), mInv,
+ entryTolerance);
+ try {
+ m.preMultiply(createSparseMatrix(bigSingular));
+ fail("Expecting illegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetVectors() {
+ RealMatrix m = createSparseMatrix(testData);
+ assertClose("get row", m.getRow(0), testDataRow1, entryTolerance);
+ assertClose("get col", m.getColumn(2), testDataCol3, entryTolerance);
+ try {
+ m.getRow(10);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ try {
+ m.getColumn(-1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // ignored
+ }
+ }
+
+ public void testGetEntry() {
+ RealMatrix m = createSparseMatrix(testData);
+ assertEquals("get entry", m.getEntry(0, 1), 2d, entryTolerance);
+ try {
+ m.getEntry(10, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ /** test examples in user guide */
+ public void testExamples() {
+ // Create a real matrix with two rows and three columns
+ double[][] matrixData = { { 1d, 2d, 3d }, { 2d, 5d, 3d } };
+ RealMatrix m = createSparseMatrix(matrixData);
+ // One more with three rows, two columns
+ double[][] matrixData2 = { { 1d, 2d }, { 2d, 5d }, { 1d, 7d } };
+ RealMatrix n = createSparseMatrix(matrixData2);
+ // Now multiply m by n
+ RealMatrix p = m.multiply(n);
+ assertEquals(2, p.getRowDimension());
+ assertEquals(2, p.getColumnDimension());
+ // Invert p
+ RealMatrix pInverse = new LUDecompositionImpl(p).getSolver().getInverse();
+ assertEquals(2, pInverse.getRowDimension());
+ assertEquals(2, pInverse.getColumnDimension());
+
+ // Solve example
+ double[][] coefficientsData = { { 2, 3, -2 }, { -1, 7, 6 },
+ { 4, -3, -5 } };
+ RealMatrix coefficients = createSparseMatrix(coefficientsData);
+ double[] constants = { 1, -2, 1 };
+ double[] solution = new LUDecompositionImpl(coefficients).getSolver().solve(constants);
+ assertEquals(2 * solution[0] + 3 * solution[1] - 2 * solution[2],
+ constants[0], 1E-12);
+ assertEquals(-1 * solution[0] + 7 * solution[1] + 6 * solution[2],
+ constants[1], 1E-12);
+ assertEquals(4 * solution[0] - 3 * solution[1] - 5 * solution[2],
+ constants[2], 1E-12);
+
+ }
+
+ // test submatrix accessors
+ public void testSubMatrix() {
+ RealMatrix m = createSparseMatrix(subTestData);
+ RealMatrix mRows23Cols00 = createSparseMatrix(subRows23Cols00);
+ RealMatrix mRows00Cols33 = createSparseMatrix(subRows00Cols33);
+ RealMatrix mRows01Cols23 = createSparseMatrix(subRows01Cols23);
+ RealMatrix mRows02Cols13 = createSparseMatrix(subRows02Cols13);
+ RealMatrix mRows03Cols12 = createSparseMatrix(subRows03Cols12);
+ RealMatrix mRows03Cols123 = createSparseMatrix(subRows03Cols123);
+ RealMatrix mRows20Cols123 = createSparseMatrix(subRows20Cols123);
+ RealMatrix mRows31Cols31 = createSparseMatrix(subRows31Cols31);
+ assertEquals("Rows23Cols00", mRows23Cols00, m.getSubMatrix(2, 3, 0, 0));
+ assertEquals("Rows00Cols33", mRows00Cols33, m.getSubMatrix(0, 0, 3, 3));
+ assertEquals("Rows01Cols23", mRows01Cols23, m.getSubMatrix(0, 1, 2, 3));
+ assertEquals("Rows02Cols13", mRows02Cols13,
+ m.getSubMatrix(new int[] { 0, 2 }, new int[] { 1, 3 }));
+ assertEquals("Rows03Cols12", mRows03Cols12,
+ m.getSubMatrix(new int[] { 0, 3 }, new int[] { 1, 2 }));
+ assertEquals("Rows03Cols123", mRows03Cols123,
+ m.getSubMatrix(new int[] { 0, 3 }, new int[] { 1, 2, 3 }));
+ assertEquals("Rows20Cols123", mRows20Cols123,
+ m.getSubMatrix(new int[] { 2, 0 }, new int[] { 1, 2, 3 }));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] { 3, 1 }, new int[] { 3, 1 }));
+ assertEquals("Rows31Cols31", mRows31Cols31,
+ m.getSubMatrix(new int[] { 3, 1 }, new int[] { 3, 1 }));
+
+ try {
+ m.getSubMatrix(1, 0, 2, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(-1, 1, 2, 2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1, 0, 2, 2);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(1, 0, 2, 4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] {}, new int[] { 0 });
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getSubMatrix(new int[] { 0 }, new int[] { 4 });
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowMatrix() {
+ RealMatrix m = createSparseMatrix(subTestData);
+ RealMatrix mRow0 = createSparseMatrix(subRow0);
+ RealMatrix mRow3 = createSparseMatrix(subRow3);
+ assertEquals("Row0", mRow0, m.getRowMatrix(0));
+ assertEquals("Row3", mRow3, m.getRowMatrix(3));
+ try {
+ m.getRowMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnMatrix() {
+ RealMatrix m = createSparseMatrix(subTestData);
+ RealMatrix mColumn1 = createSparseMatrix(subColumn1);
+ RealMatrix mColumn3 = createSparseMatrix(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnMatrix(1));
+ assertEquals("Column3", mColumn3, m.getColumnMatrix(3));
+ try {
+ m.getColumnMatrix(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnMatrix(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetRowVector() {
+ RealMatrix m = createSparseMatrix(subTestData);
+ RealVector mRow0 = new ArrayRealVector(subRow0[0]);
+ RealVector mRow3 = new ArrayRealVector(subRow3[0]);
+ assertEquals("Row0", mRow0, m.getRowVector(0));
+ assertEquals("Row3", mRow3, m.getRowVector(3));
+ try {
+ m.getRowVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getRowVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ public void testGetColumnVector() {
+ RealMatrix m = createSparseMatrix(subTestData);
+ RealVector mColumn1 = columnToVector(subColumn1);
+ RealVector mColumn3 = columnToVector(subColumn3);
+ assertEquals("Column1", mColumn1, m.getColumnVector(1));
+ assertEquals("Column3", mColumn3, m.getColumnVector(3));
+ try {
+ m.getColumnVector(-1);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ try {
+ m.getColumnVector(4);
+ fail("Expecting MatrixIndexException");
+ } catch (MatrixIndexException ex) {
+ // expected
+ }
+ }
+
+ private RealVector columnToVector(double[][] column) {
+ double[] data = new double[column.length];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = column[i][0];
+ }
+ return new ArrayRealVector(data, false);
+ }
+
+ public void testEqualsAndHashCode() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ OpenMapRealMatrix m1 = m.copy();
+ OpenMapRealMatrix mt = (OpenMapRealMatrix) m.transpose();
+ assertTrue(m.hashCode() != mt.hashCode());
+ assertEquals(m.hashCode(), m1.hashCode());
+ assertEquals(m, m);
+ assertEquals(m, m1);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals(mt));
+ assertFalse(m.equals(createSparseMatrix(bigSingular)));
+ }
+
+ public void testToString() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ assertEquals("OpenMapRealMatrix{{1.0,2.0,3.0},{2.0,5.0,3.0},{1.0,0.0,8.0}}",
+ m.toString());
+ m = new OpenMapRealMatrix(1, 1);
+ assertEquals("OpenMapRealMatrix{{0.0}}", m.toString());
+ }
+
+ public void testSetSubMatrix() throws Exception {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ m.setSubMatrix(detData2, 1, 1);
+ RealMatrix expected = createSparseMatrix(new double[][] {
+ { 1.0, 2.0, 3.0 }, { 2.0, 1.0, 3.0 }, { 1.0, 2.0, 4.0 } });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(detData2, 0, 0);
+ expected = createSparseMatrix(new double[][] {
+ { 1.0, 3.0, 3.0 }, { 2.0, 4.0, 3.0 }, { 1.0, 2.0, 4.0 } });
+ assertEquals(expected, m);
+
+ m.setSubMatrix(testDataPlus2, 0, 0);
+ expected = createSparseMatrix(new double[][] {
+ { 3.0, 4.0, 5.0 }, { 4.0, 7.0, 5.0 }, { 3.0, 2.0, 10.0 } });
+ assertEquals(expected, m);
+
+ // javadoc example
+ OpenMapRealMatrix matrix =
+ createSparseMatrix(new double[][] {
+ { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 0, 1, 2 } });
+ matrix.setSubMatrix(new double[][] { { 3, 4 }, { 5, 6 } }, 1, 1);
+ expected = createSparseMatrix(new double[][] {
+ { 1, 2, 3, 4 }, { 5, 3, 4, 8 }, { 9, 5, 6, 2 } });
+ assertEquals(expected, matrix);
+
+ // dimension overflow
+ try {
+ m.setSubMatrix(testData, 1, 1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ // dimension underflow
+ try {
+ m.setSubMatrix(testData, -1, 1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+ try {
+ m.setSubMatrix(testData, 1, -1);
+ fail("expecting MatrixIndexException");
+ } catch (MatrixIndexException e) {
+ // expected
+ }
+
+ // null
+ try {
+ m.setSubMatrix(null, 1, 1);
+ fail("expecting NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ try {
+ new OpenMapRealMatrix(0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // ragged
+ try {
+ m.setSubMatrix(new double[][] { { 1 }, { 2, 3 } }, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // empty
+ try {
+ m.setSubMatrix(new double[][] { {} }, 0, 0);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+ public void testSerial() {
+ OpenMapRealMatrix m = createSparseMatrix(testData);
+ assertEquals(m,TestUtils.serializeAndRecover(m));
+ }
+
+ // --------------- -----------------Protected methods
+
+ /** verifies that two matrices are close (1-norm) */
+ protected void assertClose(String msg, RealMatrix m, RealMatrix n,
+ double tolerance) {
+ assertTrue(msg, m.subtract(n).getNorm() < tolerance);
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, double[] m, double[] n,
+ double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors not same length");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i], n[i],
+ tolerance);
+ }
+ }
+
+ private OpenMapRealMatrix createSparseMatrix(double[][] data) {
+ OpenMapRealMatrix matrix = new OpenMapRealMatrix(data.length, data[0].length);
+ for (int row = 0; row < data.length; row++) {
+ for (int col = 0; col < data[row].length; col++) {
+ matrix.setEntry(row, col, data[row][col]);
+ }
+ }
+ return matrix;
+ }
+
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/SparseRealVectorTest.java b/src/test/java/org/apache/commons/math/linear/SparseRealVectorTest.java
new file mode 100644
index 0000000..bef7de9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/SparseRealVectorTest.java
@@ -0,0 +1,1194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Iterator;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.linear.MatrixVisitorException;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link OpenMapRealVector} class.
+ *
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class SparseRealVectorTest extends TestCase {
+
+ //
+ protected double[][] ma1 = {{1d, 2d, 3d}, {4d, 5d, 6d}, {7d, 8d, 9d}};
+ protected double[] vec1 = {1d, 2d, 3d};
+ protected double[] vec2 = {4d, 5d, 6d};
+ protected double[] vec3 = {7d, 8d, 9d};
+ protected double[] vec4 = {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d};
+ protected double[] vec5 = { -4d, 0d, 3d, 1d, -6d, 3d};
+ protected double[] vec_null = {0d, 0d, 0d};
+ protected Double[] dvec1 = {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d};
+ protected double[][] mat1 = {{1d, 2d, 3d}, {4d, 5d, 6d},{ 7d, 8d, 9d}};
+
+ // tolerances
+ protected double entryTolerance = 10E-16;
+ protected double normTolerance = 10E-14;
+
+ // Testclass to test the RealVector interface
+ // only with enough content to support the test
+ public static class SparseRealVectorTestImpl extends AbstractRealVector implements Serializable {
+
+ private static final long serialVersionUID = -6251371752518113791L;
+ /** Entries of the vector. */
+ protected double data[];
+
+ public SparseRealVectorTestImpl(double[] d) {
+ data = d.clone();
+ }
+
+ private MatrixVisitorException unsupported() {
+ return new MatrixVisitorException("Not supported, unneeded for test purposes", new Object[0]);
+ }
+
+ @Override
+ public RealVector map(UnivariateRealFunction function) throws MatrixVisitorException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapToSelf(UnivariateRealFunction function) throws MatrixVisitorException {
+ throw unsupported();
+ }
+
+ @Override
+ public Iterator<Entry> iterator() {
+ throw unsupported();
+ }
+
+ @Override
+ public AbstractRealVector copy() {
+ return new SparseRealVectorTestImpl(data);
+ }
+
+ @Override
+ public RealVector add(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector add(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector subtract(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector subtract(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAdd(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAddToSelf(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSubtract(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSubtractToSelf(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapMultiply(double d) {
+ double[] out = new double[data.length];
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i] * d;
+ }
+ return new OpenMapRealVector(out);
+ }
+
+ @Override
+ public RealVector mapMultiplyToSelf(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapDivide(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapDivideToSelf(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapPow(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapPowToSelf(double d) {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapExp() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapExpToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapExpm1() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapExpm1ToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLog() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLogToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLog10() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLog10ToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLog1p() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapLog1pToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCosh() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCoshToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSinh() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSinhToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapTanh() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapTanhToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCos() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCosToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSin() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSinToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapTan() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapTanToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAcos() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAcosToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAsin() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAsinToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAtan() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAtanToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapInv() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapInvToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAbs() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapAbsToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSqrt() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSqrtToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCbrt() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCbrtToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCeil() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapCeilToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapFloor() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapFloorToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapRint() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapRintToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSignum() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapSignumToSelf() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapUlp() {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector mapUlpToSelf() {
+ throw unsupported();
+ }
+
+ public RealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector ebeMultiply(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public RealVector ebeDivide(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector ebeDivide(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double[] getData() {
+ return data.clone();
+ }
+
+ @Override
+ public double dotProduct(RealVector v) throws IllegalArgumentException {
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * v.getEntry(i);
+ }
+ return dot;
+ }
+
+ @Override
+ public double dotProduct(double[] v) throws IllegalArgumentException {
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * v[i];
+ }
+ return dot;
+ }
+
+ @Override
+ public double getNorm() {
+ throw unsupported();
+ }
+
+ @Override
+ public double getL1Norm() {
+ throw unsupported();
+ }
+
+ @Override
+ public double getLInfNorm() {
+ throw unsupported();
+ }
+
+ @Override
+ public double getDistance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getDistance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getL1Distance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getL1Distance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getLInfDistance(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public double getLInfDistance(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector unitVector() {
+ throw unsupported();
+ }
+
+ @Override
+ public void unitize() {
+ throw unsupported();
+ }
+
+ public RealVector projection(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealVector projection(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealMatrix outerProduct(RealVector v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ @Override
+ public RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
+ throw unsupported();
+ }
+
+ public double getEntry(int index) throws MatrixIndexException {
+ return data[index];
+ }
+
+ public int getDimension() {
+ return data.length;
+ }
+
+ public RealVector append(RealVector v) {
+ throw unsupported();
+ }
+
+ public RealVector append(double d) {
+ throw unsupported();
+ }
+
+ public RealVector append(double[] a) {
+ throw unsupported();
+ }
+
+ public RealVector getSubVector(int index, int n) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ public void setEntry(int index, double value) throws MatrixIndexException {
+ data[index] = value;
+ }
+
+ @Override
+ public void setSubVector(int index, RealVector v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ @Override
+ public void setSubVector(int index, double[] v) throws MatrixIndexException {
+ throw unsupported();
+ }
+
+ @Override
+ public void set(double value) {
+ throw unsupported();
+ }
+
+ @Override
+ public double[] toArray() {
+ throw unsupported();
+ }
+
+ public boolean isNaN() {
+ throw unsupported();
+ }
+
+ public boolean isInfinite() {
+ throw unsupported();
+ }
+
+ }
+
+ public void testConstructors() {
+
+ OpenMapRealVector v0 = new OpenMapRealVector();
+ assertEquals("testData len", 0, v0.getDimension());
+
+ OpenMapRealVector v1 = new OpenMapRealVector(7);
+ assertEquals("testData len", 7, v1.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v1.getEntry(6));
+
+ OpenMapRealVector v3 = new OpenMapRealVector(vec1);
+ assertEquals("testData len", 3, v3.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v3.getEntry(1));
+
+ //SparseRealVector v4 = new SparseRealVector(vec4, 3, 2);
+ //assertEquals("testData len", 2, v4.getDimension());
+ //assertEquals("testData is 4.0 ", 4.0, v4.getEntry(0));
+ //try {
+ // new SparseRealVector(vec4, 8, 3);
+ // fail("IllegalArgumentException expected");
+ //} catch (IllegalArgumentException ex) {
+ // expected behavior
+ //}
+
+ RealVector v5_i = new OpenMapRealVector(dvec1);
+ assertEquals("testData len", 9, v5_i.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v5_i.getEntry(8));
+
+ OpenMapRealVector v5 = new OpenMapRealVector(dvec1);
+ assertEquals("testData len", 9, v5.getDimension());
+ assertEquals("testData is 9.0 ", 9.0, v5.getEntry(8));
+
+ OpenMapRealVector v7 = new OpenMapRealVector(v1);
+ assertEquals("testData len", 7, v7.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v7.getEntry(6));
+
+ SparseRealVectorTestImpl v7_i = new SparseRealVectorTestImpl(vec1);
+
+ OpenMapRealVector v7_2 = new OpenMapRealVector(v7_i);
+ assertEquals("testData len", 3, v7_2.getDimension());
+ assertEquals("testData is 0.0 ", 2.0d, v7_2.getEntry(1));
+
+ OpenMapRealVector v8 = new OpenMapRealVector(v1);
+ assertEquals("testData len", 7, v8.getDimension());
+ assertEquals("testData is 0.0 ", 0.0, v8.getEntry(6));
+
+ }
+
+ public void testDataInOut() {
+
+ OpenMapRealVector v1 = new OpenMapRealVector(vec1);
+ OpenMapRealVector v2 = new OpenMapRealVector(vec2);
+ OpenMapRealVector v4 = new OpenMapRealVector(vec4);
+ SparseRealVectorTestImpl v2_t = new SparseRealVectorTestImpl(vec2);
+
+ RealVector v_append_1 = v1.append(v2);
+ assertEquals("testData len", 6, v_append_1.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v_append_1.getEntry(3));
+
+ RealVector v_append_2 = v1.append(2.0);
+ assertEquals("testData len", 4, v_append_2.getDimension());
+ assertEquals("testData is 2.0 ", 2.0, v_append_2.getEntry(3));
+
+ RealVector v_append_3 = v1.append(vec2);
+ assertEquals("testData len", 6, v_append_3.getDimension());
+ assertEquals("testData is ", 4.0, v_append_3.getEntry(3));
+
+ RealVector v_append_4 = v1.append(v2_t);
+ assertEquals("testData len", 6, v_append_4.getDimension());
+ assertEquals("testData is 4.0 ", 4.0, v_append_4.getEntry(3));
+
+ RealVector vout5 = v4.getSubVector(3, 3);
+ assertEquals("testData len", 3, vout5.getDimension());
+ assertEquals("testData is 4.0 ", 5.0, vout5.getEntry(1));
+ try {
+ v4.getSubVector(3, 7);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ OpenMapRealVector v_set1 = v1.copy();
+ v_set1.setEntry(1, 11.0);
+ assertEquals("testData is 11.0 ", 11.0, v_set1.getEntry(1));
+ try {
+ v_set1.setEntry(3, 11.0);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ OpenMapRealVector v_set2 = v4.copy();
+ v_set2.setSubVector(3, v1);
+ assertEquals("testData is 1.0 ", 1.0, v_set2.getEntry(3));
+ assertEquals("testData is 7.0 ", 7.0, v_set2.getEntry(6));
+ try {
+ v_set2.setSubVector(7, v1);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ OpenMapRealVector v_set3 = v1.copy();
+ v_set3.set(13.0);
+ assertEquals("testData is 13.0 ", 13.0, v_set3.getEntry(2));
+
+ try {
+ v_set3.getEntry(23);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+ OpenMapRealVector v_set4 = v4.copy();
+ v_set4.setSubVector(3, v2_t);
+ assertEquals("testData is 1.0 ", 4.0, v_set4.getEntry(3));
+ assertEquals("testData is 7.0 ", 7.0, v_set4.getEntry(6));
+ try {
+ v_set4.setSubVector(7, v2_t);
+ fail("MatrixIndexException expected");
+ } catch (MatrixIndexException ex) {
+ // expected behavior
+ }
+
+
+ }
+
+ public void testMapFunctions() {
+ OpenMapRealVector v1 = new OpenMapRealVector(vec1);
+
+ //octave = v1 .+ 2.0
+ RealVector v_mapAdd = v1.mapAdd(2.0d);
+ double[] result_mapAdd = {3d, 4d, 5d};
+ assertClose("compare vectors" ,result_mapAdd,v_mapAdd.getData(),normTolerance);
+
+ //octave = v1 .+ 2.0
+ RealVector v_mapAddToSelf = v1.copy();
+ v_mapAddToSelf.mapAddToSelf(2.0d);
+ double[] result_mapAddToSelf = {3d, 4d, 5d};
+ assertClose("compare vectors" ,result_mapAddToSelf,v_mapAddToSelf.getData(),normTolerance);
+
+ //octave = v1 .- 2.0
+ RealVector v_mapSubtract = v1.mapSubtract(2.0d);
+ double[] result_mapSubtract = {-1d, 0d, 1d};
+ assertClose("compare vectors" ,result_mapSubtract,v_mapSubtract.getData(),normTolerance);
+
+ //octave = v1 .- 2.0
+ RealVector v_mapSubtractToSelf = v1.copy();
+ v_mapSubtractToSelf.mapSubtractToSelf(2.0d);
+ double[] result_mapSubtractToSelf = {-1d, 0d, 1d};
+ assertClose("compare vectors" ,result_mapSubtractToSelf,v_mapSubtractToSelf.getData(),normTolerance);
+
+ //octave = v1 .* 2.0
+ RealVector v_mapMultiply = v1.mapMultiply(2.0d);
+ double[] result_mapMultiply = {2d, 4d, 6d};
+ assertClose("compare vectors" ,result_mapMultiply,v_mapMultiply.getData(),normTolerance);
+
+ //octave = v1 .* 2.0
+ RealVector v_mapMultiplyToSelf = v1.copy();
+ v_mapMultiplyToSelf.mapMultiplyToSelf(2.0d);
+ double[] result_mapMultiplyToSelf = {2d, 4d, 6d};
+ assertClose("compare vectors" ,result_mapMultiplyToSelf,v_mapMultiplyToSelf.getData(),normTolerance);
+
+ //octave = v1 ./ 2.0
+ RealVector v_mapDivide = v1.mapDivide(2.0d);
+ double[] result_mapDivide = {.5d, 1d, 1.5d};
+ assertClose("compare vectors" ,result_mapDivide,v_mapDivide.getData(),normTolerance);
+
+ //octave = v1 ./ 2.0
+ RealVector v_mapDivideToSelf = v1.copy();
+ v_mapDivideToSelf.mapDivideToSelf(2.0d);
+ double[] result_mapDivideToSelf = {.5d, 1d, 1.5d};
+ assertClose("compare vectors" ,result_mapDivideToSelf,v_mapDivideToSelf.getData(),normTolerance);
+
+ //octave = v1 .^ 2.0
+ RealVector v_mapPow = v1.mapPow(2.0d);
+ double[] result_mapPow = {1d, 4d, 9d};
+ assertClose("compare vectors" ,result_mapPow,v_mapPow.getData(),normTolerance);
+
+ //octave = v1 .^ 2.0
+ RealVector v_mapPowToSelf = v1.copy();
+ v_mapPowToSelf.mapPowToSelf(2.0d);
+ double[] result_mapPowToSelf = {1d, 4d, 9d};
+ assertClose("compare vectors" ,result_mapPowToSelf,v_mapPowToSelf.getData(),normTolerance);
+
+ //octave = exp(v1)
+ RealVector v_mapExp = v1.mapExp();
+ double[] result_mapExp = {2.718281828459045e+00d,7.389056098930650e+00d, 2.008553692318767e+01d};
+ assertClose("compare vectors" ,result_mapExp,v_mapExp.getData(),normTolerance);
+
+ //octave = exp(v1)
+ RealVector v_mapExpToSelf = v1.copy();
+ v_mapExpToSelf.mapExpToSelf();
+ double[] result_mapExpToSelf = {2.718281828459045e+00d,7.389056098930650e+00d, 2.008553692318767e+01d};
+ assertClose("compare vectors" ,result_mapExpToSelf,v_mapExpToSelf.getData(),normTolerance);
+
+
+ //octave = ???
+ RealVector v_mapExpm1 = v1.mapExpm1();
+ double[] result_mapExpm1 = {1.718281828459045d,6.38905609893065d, 19.085536923187668d};
+ assertClose("compare vectors" ,result_mapExpm1,v_mapExpm1.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapExpm1ToSelf = v1.copy();
+ v_mapExpm1ToSelf.mapExpm1ToSelf();
+ double[] result_mapExpm1ToSelf = {1.718281828459045d,6.38905609893065d, 19.085536923187668d};
+ assertClose("compare vectors" ,result_mapExpm1ToSelf,v_mapExpm1ToSelf.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLog = v1.mapLog();
+ double[] result_mapLog = {0d,6.931471805599453e-01d, 1.098612288668110e+00d};
+ assertClose("compare vectors" ,result_mapLog,v_mapLog.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLogToSelf = v1.copy();
+ v_mapLogToSelf.mapLogToSelf();
+ double[] result_mapLogToSelf = {0d,6.931471805599453e-01d, 1.098612288668110e+00d};
+ assertClose("compare vectors" ,result_mapLogToSelf,v_mapLogToSelf.getData(),normTolerance);
+
+ //octave = log10(v1)
+ RealVector v_mapLog10 = v1.mapLog10();
+ double[] result_mapLog10 = {0d,3.010299956639812e-01d, 4.771212547196624e-01d};
+ assertClose("compare vectors" ,result_mapLog10,v_mapLog10.getData(),normTolerance);
+
+ //octave = log(v1)
+ RealVector v_mapLog10ToSelf = v1.copy();
+ v_mapLog10ToSelf.mapLog10ToSelf();
+ double[] result_mapLog10ToSelf = {0d,3.010299956639812e-01d, 4.771212547196624e-01d};
+ assertClose("compare vectors" ,result_mapLog10ToSelf,v_mapLog10ToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapLog1p = v1.mapLog1p();
+ double[] result_mapLog1p = {0.6931471805599453d,1.0986122886681096d,1.3862943611198906d};
+ assertClose("compare vectors" ,result_mapLog1p,v_mapLog1p.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapLog1pToSelf = v1.copy();
+ v_mapLog1pToSelf.mapLog1pToSelf();
+ double[] result_mapLog1pToSelf = {0.6931471805599453d,1.0986122886681096d,1.3862943611198906d};
+ assertClose("compare vectors" ,result_mapLog1pToSelf,v_mapLog1pToSelf.getData(),normTolerance);
+
+ //octave = cosh(v1)
+ RealVector v_mapCosh = v1.mapCosh();
+ double[] result_mapCosh = {1.543080634815244e+00d,3.762195691083631e+00d, 1.006766199577777e+01d};
+ assertClose("compare vectors" ,result_mapCosh,v_mapCosh.getData(),normTolerance);
+
+ //octave = cosh(v1)
+ RealVector v_mapCoshToSelf = v1.copy();
+ v_mapCoshToSelf.mapCoshToSelf();
+ double[] result_mapCoshToSelf = {1.543080634815244e+00d,3.762195691083631e+00d, 1.006766199577777e+01d};
+ assertClose("compare vectors" ,result_mapCoshToSelf,v_mapCoshToSelf.getData(),normTolerance);
+
+ //octave = sinh(v1)
+ RealVector v_mapSinh = v1.mapSinh();
+ double[] result_mapSinh = {1.175201193643801e+00d,3.626860407847019e+00d, 1.001787492740990e+01d};
+ assertClose("compare vectors" ,result_mapSinh,v_mapSinh.getData(),normTolerance);
+
+ //octave = sinh(v1)
+ RealVector v_mapSinhToSelf = v1.copy();
+ v_mapSinhToSelf.mapSinhToSelf();
+ double[] result_mapSinhToSelf = {1.175201193643801e+00d,3.626860407847019e+00d, 1.001787492740990e+01d};
+ assertClose("compare vectors" ,result_mapSinhToSelf,v_mapSinhToSelf.getData(),normTolerance);
+
+ //octave = tanh(v1)
+ RealVector v_mapTanh = v1.mapTanh();
+ double[] result_mapTanh = {7.615941559557649e-01d,9.640275800758169e-01d,9.950547536867305e-01d};
+ assertClose("compare vectors" ,result_mapTanh,v_mapTanh.getData(),normTolerance);
+
+ //octave = tanh(v1)
+ RealVector v_mapTanhToSelf = v1.copy();
+ v_mapTanhToSelf.mapTanhToSelf();
+ double[] result_mapTanhToSelf = {7.615941559557649e-01d,9.640275800758169e-01d,9.950547536867305e-01d};
+ assertClose("compare vectors" ,result_mapTanhToSelf,v_mapTanhToSelf.getData(),normTolerance);
+
+ //octave = cos(v1)
+ RealVector v_mapCos = v1.mapCos();
+ double[] result_mapCos = {5.403023058681398e-01d,-4.161468365471424e-01d, -9.899924966004454e-01d};
+ assertClose("compare vectors" ,result_mapCos,v_mapCos.getData(),normTolerance);
+
+ //octave = cos(v1)
+ RealVector v_mapCosToSelf = v1.copy();
+ v_mapCosToSelf.mapCosToSelf();
+ double[] result_mapCosToSelf = {5.403023058681398e-01d,-4.161468365471424e-01d, -9.899924966004454e-01d};
+ assertClose("compare vectors" ,result_mapCosToSelf,v_mapCosToSelf.getData(),normTolerance);
+
+ //octave = sin(v1)
+ RealVector v_mapSin = v1.mapSin();
+ double[] result_mapSin = {8.414709848078965e-01d,9.092974268256817e-01d,1.411200080598672e-01d};
+ assertClose("compare vectors" ,result_mapSin,v_mapSin.getData(),normTolerance);
+
+ //octave = sin(v1)
+ RealVector v_mapSinToSelf = v1.copy();
+ v_mapSinToSelf.mapSinToSelf();
+ double[] result_mapSinToSelf = {8.414709848078965e-01d,9.092974268256817e-01d,1.411200080598672e-01d};
+ assertClose("compare vectors" ,result_mapSinToSelf,v_mapSinToSelf.getData(),normTolerance);
+
+ //octave = tan(v1)
+ RealVector v_mapTan = v1.mapTan();
+ double[] result_mapTan = {1.557407724654902e+00d,-2.185039863261519e+00d,-1.425465430742778e-01d};
+ assertClose("compare vectors" ,result_mapTan,v_mapTan.getData(),normTolerance);
+
+ //octave = tan(v1)
+ RealVector v_mapTanToSelf = v1.copy();
+ v_mapTanToSelf.mapTanToSelf();
+ double[] result_mapTanToSelf = {1.557407724654902e+00d,-2.185039863261519e+00d,-1.425465430742778e-01d};
+ assertClose("compare vectors" ,result_mapTanToSelf,v_mapTanToSelf.getData(),normTolerance);
+
+ double[] vat_a = {0d, 0.5d, 1.0d};
+ OpenMapRealVector vat = new OpenMapRealVector(vat_a);
+
+ //octave = acos(vat)
+ RealVector v_mapAcos = vat.mapAcos();
+ double[] result_mapAcos = {1.570796326794897e+00d,1.047197551196598e+00d, 0.0d};
+ assertClose("compare vectors" ,result_mapAcos,v_mapAcos.getData(),normTolerance);
+
+ //octave = acos(vat)
+ RealVector v_mapAcosToSelf = vat.copy();
+ v_mapAcosToSelf.mapAcosToSelf();
+ double[] result_mapAcosToSelf = {1.570796326794897e+00d,1.047197551196598e+00d, 0.0d};
+ assertClose("compare vectors" ,result_mapAcosToSelf,v_mapAcosToSelf.getData(),normTolerance);
+
+ //octave = asin(vat)
+ RealVector v_mapAsin = vat.mapAsin();
+ double[] result_mapAsin = {0.0d,5.235987755982989e-01d,1.570796326794897e+00d};
+ assertClose("compare vectors" ,result_mapAsin,v_mapAsin.getData(),normTolerance);
+
+ //octave = asin(vat)
+ RealVector v_mapAsinToSelf = vat.copy();
+ v_mapAsinToSelf.mapAsinToSelf();
+ double[] result_mapAsinToSelf = {0.0d,5.235987755982989e-01d,1.570796326794897e+00d};
+ assertClose("compare vectors" ,result_mapAsinToSelf,v_mapAsinToSelf.getData(),normTolerance);
+
+ //octave = atan(vat)
+ RealVector v_mapAtan = vat.mapAtan();
+ double[] result_mapAtan = {0.0d,4.636476090008061e-01d,7.853981633974483e-01d};
+ assertClose("compare vectors" ,result_mapAtan,v_mapAtan.getData(),normTolerance);
+
+ //octave = atan(vat)
+ RealVector v_mapAtanToSelf = vat.copy();
+ v_mapAtanToSelf.mapAtanToSelf();
+ double[] result_mapAtanToSelf = {0.0d,4.636476090008061e-01d,7.853981633974483e-01d};
+ assertClose("compare vectors" ,result_mapAtanToSelf,v_mapAtanToSelf.getData(),normTolerance);
+
+ //octave = v1 .^-1
+ RealVector v_mapInv = v1.mapInv();
+ double[] result_mapInv = {1d,0.5d,3.333333333333333e-01d};
+ assertClose("compare vectors" ,result_mapInv,v_mapInv.getData(),normTolerance);
+
+ //octave = v1 .^-1
+ RealVector v_mapInvToSelf = v1.copy();
+ v_mapInvToSelf.mapInvToSelf();
+ double[] result_mapInvToSelf = {1d,0.5d,3.333333333333333e-01d};
+ assertClose("compare vectors" ,result_mapInvToSelf,v_mapInvToSelf.getData(),normTolerance);
+
+ double[] abs_a = {-1.0d, 0.0d, 1.0d};
+ OpenMapRealVector abs_v = new OpenMapRealVector(abs_a);
+
+ //octave = abs(abs_v)
+ RealVector v_mapAbs = abs_v.mapAbs();
+ double[] result_mapAbs = {1d,0d,1d};
+ assertClose("compare vectors" ,result_mapAbs,v_mapAbs.getData(),normTolerance);
+
+ //octave = abs(abs_v)
+ RealVector v_mapAbsToSelf = abs_v.copy();
+ v_mapAbsToSelf.mapAbsToSelf();
+ double[] result_mapAbsToSelf = {1d,0d,1d};
+ assertClose("compare vectors" ,result_mapAbsToSelf,v_mapAbsToSelf.getData(),normTolerance);
+
+ //octave = sqrt(v1)
+ RealVector v_mapSqrt = v1.mapSqrt();
+ double[] result_mapSqrt = {1d,1.414213562373095e+00d,1.732050807568877e+00d};
+ assertClose("compare vectors" ,result_mapSqrt,v_mapSqrt.getData(),normTolerance);
+
+ //octave = sqrt(v1)
+ RealVector v_mapSqrtToSelf = v1.copy();
+ v_mapSqrtToSelf.mapSqrtToSelf();
+ double[] result_mapSqrtToSelf = {1d,1.414213562373095e+00d,1.732050807568877e+00d};
+ assertClose("compare vectors" ,result_mapSqrtToSelf,v_mapSqrtToSelf.getData(),normTolerance);
+
+ double[] cbrt_a = {-2.0d, 0.0d, 2.0d};
+ OpenMapRealVector cbrt_v = new OpenMapRealVector(cbrt_a);
+
+ //octave = ???
+ RealVector v_mapCbrt = cbrt_v.mapCbrt();
+ double[] result_mapCbrt = {-1.2599210498948732d,0d,1.2599210498948732d};
+ assertClose("compare vectors" ,result_mapCbrt,v_mapCbrt.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapCbrtToSelf = cbrt_v.copy();
+ v_mapCbrtToSelf.mapCbrtToSelf();
+ double[] result_mapCbrtToSelf = {-1.2599210498948732d,0d,1.2599210498948732d};
+ assertClose("compare vectors" ,result_mapCbrtToSelf,v_mapCbrtToSelf.getData(),normTolerance);
+
+ double[] ceil_a = {-1.1d, 0.9d, 1.1d};
+ OpenMapRealVector ceil_v = new OpenMapRealVector(ceil_a);
+
+ //octave = ceil(ceil_v)
+ RealVector v_mapCeil = ceil_v.mapCeil();
+ double[] result_mapCeil = {-1d,1d,2d};
+ assertClose("compare vectors" ,result_mapCeil,v_mapCeil.getData(),normTolerance);
+
+ //octave = ceil(ceil_v)
+ RealVector v_mapCeilToSelf = ceil_v.copy();
+ v_mapCeilToSelf.mapCeilToSelf();
+ double[] result_mapCeilToSelf = {-1d,1d,2d};
+ assertClose("compare vectors" ,result_mapCeilToSelf,v_mapCeilToSelf.getData(),normTolerance);
+
+ //octave = floor(ceil_v)
+ RealVector v_mapFloor = ceil_v.mapFloor();
+ double[] result_mapFloor = {-2d,0d,1d};
+ assertClose("compare vectors" ,result_mapFloor,v_mapFloor.getData(),normTolerance);
+
+ //octave = floor(ceil_v)
+ RealVector v_mapFloorToSelf = ceil_v.copy();
+ v_mapFloorToSelf.mapFloorToSelf();
+ double[] result_mapFloorToSelf = {-2d,0d,1d};
+ assertClose("compare vectors" ,result_mapFloorToSelf,v_mapFloorToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapRint = ceil_v.mapRint();
+ double[] result_mapRint = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapRint,v_mapRint.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapRintToSelf = ceil_v.copy();
+ v_mapRintToSelf.mapRintToSelf();
+ double[] result_mapRintToSelf = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapRintToSelf,v_mapRintToSelf.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapSignum = ceil_v.mapSignum();
+ double[] result_mapSignum = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapSignum,v_mapSignum.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapSignumToSelf = ceil_v.copy();
+ v_mapSignumToSelf.mapSignumToSelf();
+ double[] result_mapSignumToSelf = {-1d,1d,1d};
+ assertClose("compare vectors" ,result_mapSignumToSelf,v_mapSignumToSelf.getData(),normTolerance);
+
+
+ // Is with the used resolutions of limited value as test
+ //octave = ???
+ RealVector v_mapUlp = ceil_v.mapUlp();
+ double[] result_mapUlp = {2.220446049250313E-16d,1.1102230246251565E-16d,2.220446049250313E-16d};
+ assertClose("compare vectors" ,result_mapUlp,v_mapUlp.getData(),normTolerance);
+
+ //octave = ???
+ RealVector v_mapUlpToSelf = ceil_v.copy();
+ v_mapUlpToSelf.mapUlpToSelf();
+ double[] result_mapUlpToSelf = {2.220446049250313E-16d,1.1102230246251565E-16d,2.220446049250313E-16d};
+ assertClose("compare vectors" ,result_mapUlpToSelf,v_mapUlpToSelf.getData(),normTolerance);
+
+ }
+
+ public void testBasicFunctions() {
+ OpenMapRealVector v1 = new OpenMapRealVector(vec1);
+ OpenMapRealVector v2 = new OpenMapRealVector(vec2);
+ OpenMapRealVector v5 = new OpenMapRealVector(vec5);
+ OpenMapRealVector v_null = new OpenMapRealVector(vec_null);
+
+ SparseRealVectorTestImpl v2_t = new SparseRealVectorTestImpl(vec2);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] A --> 8.4261497731763586307
+ double d_getNorm = v5.getNorm();
+ assertEquals("compare values ", 8.4261497731763586307, d_getNorm);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] vN --> 17
+ double d_getL1Norm = v5.getL1Norm();
+ assertEquals("compare values ", 17.0, d_getL1Norm);
+
+ // emacs calc: [-4, 0, 3, 1, -6, 3] vn --> 6
+ double d_getLInfNorm = v5.getLInfNorm();
+ assertEquals("compare values ", 6.0, d_getLInfNorm);
+
+ //octave = sqrt(sumsq(v1-v2))
+ double dist = v1.getDistance(v2);
+ assertEquals("compare values ",v1.subtract(v2).getNorm(), dist );
+
+ //octave = sqrt(sumsq(v1-v2))
+ double dist_2 = v1.getDistance(v2_t);
+ assertEquals("compare values ", v1.subtract(v2).getNorm(),dist_2 );
+
+ //octave = ???
+ double d_getL1Distance = v1. getL1Distance(v2);
+ assertEquals("compare values ",9d, d_getL1Distance );
+
+ double d_getL1Distance_2 = v1. getL1Distance(v2_t);
+ assertEquals("compare values ",9d, d_getL1Distance_2 );
+
+ //octave = ???
+ double d_getLInfDistance = v1. getLInfDistance(v2);
+ assertEquals("compare values ",3d, d_getLInfDistance );
+
+ double d_getLInfDistance_2 = v1. getLInfDistance(v2_t);
+ assertEquals("compare values ",3d, d_getLInfDistance_2 );
+
+ //octave = v1 + v2
+ OpenMapRealVector v_add = v1.add(v2);
+ double[] result_add = {5d, 7d, 9d};
+ assertClose("compare vect" ,v_add.getData(),result_add,normTolerance);
+
+ SparseRealVectorTestImpl vt2 = new SparseRealVectorTestImpl(vec2);
+ RealVector v_add_i = v1.add(vt2);
+ double[] result_add_i = {5d, 7d, 9d};
+ assertClose("compare vect" ,v_add_i.getData(),result_add_i,normTolerance);
+
+ //octave = v1 - v2
+ OpenMapRealVector v_subtract = v1.subtract(v2);
+ double[] result_subtract = {-3d, -3d, -3d};
+ assertClose("compare vect" ,v_subtract.getData(),result_subtract,normTolerance);
+
+ RealVector v_subtract_i = v1.subtract(vt2);
+ double[] result_subtract_i = {-3d, -3d, -3d};
+ assertClose("compare vect" ,v_subtract_i.getData(),result_subtract_i,normTolerance);
+
+ // octave v1 .* v2
+ RealVector v_ebeMultiply = v1.ebeMultiply(v2);
+ double[] result_ebeMultiply = {4d, 10d, 18d};
+ assertClose("compare vect" ,v_ebeMultiply.getData(),result_ebeMultiply,normTolerance);
+
+ RealVector v_ebeMultiply_2 = v1.ebeMultiply(v2_t);
+ double[] result_ebeMultiply_2 = {4d, 10d, 18d};
+ assertClose("compare vect" ,v_ebeMultiply_2.getData(),result_ebeMultiply_2,normTolerance);
+
+ // octave v1 ./ v2
+ RealVector v_ebeDivide = v1.ebeDivide(v2);
+ double[] result_ebeDivide = {0.25d, 0.4d, 0.5d};
+ assertClose("compare vect" ,v_ebeDivide.getData(),result_ebeDivide,normTolerance);
+
+ RealVector v_ebeDivide_2 = v1.ebeDivide(v2_t);
+ double[] result_ebeDivide_2 = {0.25d, 0.4d, 0.5d};
+ assertClose("compare vect" ,v_ebeDivide_2.getData(),result_ebeDivide_2,normTolerance);
+
+ // octave dot(v1,v2)
+ double dot = v1.dotProduct(v2);
+ assertEquals("compare val ",32d, dot);
+
+ // octave dot(v1,v2_t)
+ double dot_2 = v1.dotProduct(v2_t);
+ assertEquals("compare val ",32d, dot_2);
+
+ RealMatrix m_outerProduct = v1.outerProduct(v2);
+ assertEquals("compare val ",4d, m_outerProduct.getEntry(0,0));
+
+ RealMatrix m_outerProduct_2 = v1.outerProduct(v2_t);
+ assertEquals("compare val ",4d, m_outerProduct_2.getEntry(0,0));
+
+ RealVector v_unitVector = v1.unitVector();
+ RealVector v_unitVector_2 = v1.mapDivide(v1.getNorm());
+ assertClose("compare vect" ,v_unitVector.getData(),v_unitVector_2.getData(),normTolerance);
+
+ try {
+ v_null.unitVector();
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected behavior
+ }
+
+ OpenMapRealVector v_unitize = v1.copy();
+ v_unitize.unitize();
+ assertClose("compare vect" ,v_unitVector_2.getData(),v_unitize.getData(),normTolerance);
+ try {
+ v_null.unitize();
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected behavior
+ }
+
+ RealVector v_projection = v1.projection(v2);
+ double[] result_projection = {1.662337662337662, 2.0779220779220777, 2.493506493506493};
+ assertClose("compare vect", v_projection.getData(), result_projection, normTolerance);
+
+ RealVector v_projection_2 = v1.projection(v2_t);
+ double[] result_projection_2 = {1.662337662337662, 2.0779220779220777, 2.493506493506493};
+ assertClose("compare vect", v_projection_2.getData(), result_projection_2, normTolerance);
+
+ }
+
+ public void testMisc() {
+ OpenMapRealVector v1 = new OpenMapRealVector(vec1);
+
+ String out1 = v1.toString();
+ assertTrue("some output ", out1.length()!=0);
+ try {
+ v1.checkVectorDimensions(2);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected behavior
+ }
+
+
+ }
+
+ public void testPredicates() {
+
+ OpenMapRealVector v = new OpenMapRealVector(new double[] { 0, 1, 2 });
+
+ assertFalse(v.isNaN());
+ v.setEntry(1, Double.NaN);
+ assertTrue(v.isNaN());
+
+ assertFalse(v.isInfinite());
+ v.setEntry(0, Double.POSITIVE_INFINITY);
+ assertFalse(v.isInfinite()); // NaN has higher priority than infinity
+ v.setEntry(1, 1);
+ assertTrue(v.isInfinite());
+
+ v.setEntry(0, 0);
+ assertEquals(v, new OpenMapRealVector(new double[] { 0, 1, 2 }));
+ assertNotSame(v, new OpenMapRealVector(new double[] { 0, 1, 2 + FastMath.ulp(2)}));
+ assertNotSame(v, new OpenMapRealVector(new double[] { 0, 1, 2, 3 }));
+
+ }
+
+ public void testSerial() {
+ OpenMapRealVector v = new OpenMapRealVector(new double[] { 0, 1, 2 });
+ assertEquals(v,TestUtils.serializeAndRecover(v));
+ }
+
+ /** verifies that two vectors are close (sup norm) */
+ protected void assertClose(String msg, double[] m, double[] n,
+ double tolerance) {
+ if (m.length != n.length) {
+ fail("vectors have different lengths");
+ }
+ for (int i = 0; i < m.length; i++) {
+ assertEquals(msg + " " + i + " elements differ", m[i],n[i],tolerance);
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/linear/TriDiagonalTransformerTest.java b/src/test/java/org/apache/commons/math/linear/TriDiagonalTransformerTest.java
new file mode 100644
index 0000000..babd0b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/linear/TriDiagonalTransformerTest.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.linear;
+
+import java.util.Arrays;
+
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public class TriDiagonalTransformerTest extends TestCase {
+
+ private double[][] testSquare5 = {
+ { 1, 2, 3, 1, 1 },
+ { 2, 1, 1, 3, 1 },
+ { 3, 1, 1, 1, 2 },
+ { 1, 3, 1, 2, 1 },
+ { 1, 1, 2, 1, 3 }
+ };
+
+ private double[][] testSquare3 = {
+ { 1, 3, 4 },
+ { 3, 2, 2 },
+ { 4, 2, 0 }
+ };
+
+ public TriDiagonalTransformerTest(String name) {
+ super(name);
+ }
+
+ public void testNonSquare() {
+ try {
+ new TriDiagonalTransformer(MatrixUtils.createRealMatrix(new double[3][2]));
+ fail("an exception should have been thrown");
+ } catch (InvalidMatrixException ime) {
+ // expected behavior
+ }
+ }
+
+ public void testAEqualQTQt() {
+ checkAEqualQTQt(MatrixUtils.createRealMatrix(testSquare5));
+ checkAEqualQTQt(MatrixUtils.createRealMatrix(testSquare3));
+ }
+
+ private void checkAEqualQTQt(RealMatrix matrix) {
+ TriDiagonalTransformer transformer = new TriDiagonalTransformer(matrix);
+ RealMatrix q = transformer.getQ();
+ RealMatrix qT = transformer.getQT();
+ RealMatrix t = transformer.getT();
+ double norm = q.multiply(t).multiply(qT).subtract(matrix).getNorm();
+ assertEquals(0, norm, 4.0e-15);
+ }
+
+ public void testNoAccessBelowDiagonal() {
+ checkNoAccessBelowDiagonal(testSquare5);
+ checkNoAccessBelowDiagonal(testSquare3);
+ }
+
+ private void checkNoAccessBelowDiagonal(double[][] data) {
+ double[][] modifiedData = new double[data.length][];
+ for (int i = 0; i < data.length; ++i) {
+ modifiedData[i] = data[i].clone();
+ Arrays.fill(modifiedData[i], 0, i, Double.NaN);
+ }
+ RealMatrix matrix = MatrixUtils.createRealMatrix(modifiedData);
+ TriDiagonalTransformer transformer = new TriDiagonalTransformer(matrix);
+ RealMatrix q = transformer.getQ();
+ RealMatrix qT = transformer.getQT();
+ RealMatrix t = transformer.getT();
+ double norm = q.multiply(t).multiply(qT).subtract(MatrixUtils.createRealMatrix(data)).getNorm();
+ assertEquals(0, norm, 4.0e-15);
+ }
+
+ public void testQOrthogonal() {
+ checkOrthogonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare5)).getQ());
+ checkOrthogonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare3)).getQ());
+ }
+
+ public void testQTOrthogonal() {
+ checkOrthogonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare5)).getQT());
+ checkOrthogonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare3)).getQT());
+ }
+
+ private void checkOrthogonal(RealMatrix m) {
+ RealMatrix mTm = m.transpose().multiply(m);
+ RealMatrix id = MatrixUtils.createRealIdentityMatrix(mTm.getRowDimension());
+ assertEquals(0, mTm.subtract(id).getNorm(), 1.0e-15);
+ }
+
+ public void testTTriDiagonal() {
+ checkTriDiagonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare5)).getT());
+ checkTriDiagonal(new TriDiagonalTransformer(MatrixUtils.createRealMatrix(testSquare3)).getT());
+ }
+
+ private void checkTriDiagonal(RealMatrix m) {
+ final int rows = m.getRowDimension();
+ final int cols = m.getColumnDimension();
+ for (int i = 0; i < rows; ++i) {
+ for (int j = 0; j < cols; ++j) {
+ if ((i < j - 1) || (i > j + 1)) {
+ assertEquals(0, m.getEntry(i, j), 1.0e-16);
+ }
+ }
+ }
+ }
+
+ public void testMatricesValues5() {
+ checkMatricesValues(testSquare5,
+ new double[][] {
+ { 1.0, 0.0, 0.0, 0.0, 0.0 },
+ { 0.0, -0.5163977794943222, 0.016748280772542083, 0.839800693771262, 0.16669620021405473 },
+ { 0.0, -0.7745966692414833, -0.4354553000860955, -0.44989322880603355, -0.08930153582895772 },
+ { 0.0, -0.2581988897471611, 0.6364346693566014, -0.30263204032131164, 0.6608313651342882 },
+ { 0.0, -0.2581988897471611, 0.6364346693566009, -0.027289660803112598, -0.7263191580755246 }
+ },
+ new double[] { 1, 4.4, 1.433099579242636, -0.89537362758743, 2.062274048344794 },
+ new double[] { -FastMath.sqrt(15), -3.0832882879592476, 0.6082710842351517, 1.1786086405912128 });
+ }
+
+ public void testMatricesValues3() {
+ checkMatricesValues(testSquare3,
+ new double[][] {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, -0.6, 0.8 },
+ { 0.0, -0.8, -0.6 },
+ },
+ new double[] { 1, 2.64, -0.64 },
+ new double[] { -5, -1.52 });
+ }
+
+ private void checkMatricesValues(double[][] matrix, double[][] qRef,
+ double[] mainDiagnonal,
+ double[] secondaryDiagonal) {
+ TriDiagonalTransformer transformer =
+ new TriDiagonalTransformer(MatrixUtils.createRealMatrix(matrix));
+
+ // check values against known references
+ RealMatrix q = transformer.getQ();
+ assertEquals(0, q.subtract(MatrixUtils.createRealMatrix(qRef)).getNorm(), 1.0e-14);
+
+ RealMatrix t = transformer.getT();
+ double[][] tData = new double[mainDiagnonal.length][mainDiagnonal.length];
+ for (int i = 0; i < mainDiagnonal.length; ++i) {
+ tData[i][i] = mainDiagnonal[i];
+ if (i > 0) {
+ tData[i][i - 1] = secondaryDiagonal[i - 1];
+ }
+ if (i < secondaryDiagonal.length) {
+ tData[i][i + 1] = secondaryDiagonal[i];
+ }
+ }
+ assertEquals(0, t.subtract(MatrixUtils.createRealMatrix(tData)).getNorm(), 1.0e-14);
+
+ // check the same cached instance is returned the second time
+ assertTrue(q == transformer.getQ());
+ assertTrue(t == transformer.getT());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/ContinuousOutputModelTest.java b/src/test/java/org/apache/commons/math/ode/ContinuousOutputModelTest.java
new file mode 100644
index 0000000..dbaf2d0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/ContinuousOutputModelTest.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;
+
+import junit.framework.*;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator;
+import org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class ContinuousOutputModelTest
+ extends TestCase {
+
+ public ContinuousOutputModelTest(String name) {
+ super(name);
+ pb = null;
+ integ = null;
+ }
+
+ public void testBoundaries()
+ throws DerivativeException, IntegratorException {
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ ContinuousOutputModel cm = (ContinuousOutputModel) integ.getStepHandlers().iterator().next();
+ cm.setInterpolatedTime(2.0 * pb.getInitialTime() - pb.getFinalTime());
+ cm.setInterpolatedTime(2.0 * pb.getFinalTime() - pb.getInitialTime());
+ cm.setInterpolatedTime(0.5 * (pb.getFinalTime() + pb.getInitialTime()));
+ }
+
+ public void testRandomAccess()
+ throws DerivativeException, IntegratorException {
+
+ ContinuousOutputModel cm = new ContinuousOutputModel();
+ integ.addStepHandler(cm);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 1.0e-9);
+
+ }
+
+ public void testModelsMerging()
+ throws DerivativeException, IntegratorException {
+
+ // theoretical solution: y[0] = cos(t), y[1] = sin(t)
+ FirstOrderDifferentialEquations problem =
+ new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = 2472449657345878299L;
+ public void computeDerivatives(double t, double[] y, double[] dot)
+ throws DerivativeException {
+ dot[0] = -y[1];
+ dot[1] = y[0];
+ }
+ public int getDimension() {
+ return 2;
+ }
+ };
+
+ // integrate backward from π to 0;
+ ContinuousOutputModel cm1 = new ContinuousOutputModel();
+ FirstOrderIntegrator integ1 =
+ new DormandPrince853Integrator(0, 1.0, 1.0e-8, 1.0e-8);
+ integ1.addStepHandler(cm1);
+ integ1.integrate(problem, FastMath.PI, new double[] { -1.0, 0.0 },
+ 0, new double[2]);
+
+ // integrate backward from 2π to π
+ ContinuousOutputModel cm2 = new ContinuousOutputModel();
+ FirstOrderIntegrator integ2 =
+ new DormandPrince853Integrator(0, 0.1, 1.0e-12, 1.0e-12);
+ integ2.addStepHandler(cm2);
+ integ2.integrate(problem, 2.0 * FastMath.PI, new double[] { 1.0, 0.0 },
+ FastMath.PI, new double[2]);
+
+ // merge the two half circles
+ ContinuousOutputModel cm = new ContinuousOutputModel();
+ cm.append(cm2);
+ cm.append(new ContinuousOutputModel());
+ cm.append(cm1);
+
+ // check circle
+ assertEquals(2.0 * FastMath.PI, cm.getInitialTime(), 1.0e-12);
+ assertEquals(0, cm.getFinalTime(), 1.0e-12);
+ assertEquals(cm.getFinalTime(), cm.getInterpolatedTime(), 1.0e-12);
+ for (double t = 0; t < 2.0 * FastMath.PI; t += 0.1) {
+ cm.setInterpolatedTime(t);
+ double[] y = cm.getInterpolatedState();
+ assertEquals(FastMath.cos(t), y[0], 1.0e-7);
+ assertEquals(FastMath.sin(t), y[1], 1.0e-7);
+ }
+
+ }
+
+ public void testErrorConditions()
+ throws DerivativeException {
+
+ ContinuousOutputModel cm = new ContinuousOutputModel();
+ cm.handleStep(buildInterpolator(0, new double[] { 0.0, 1.0, -2.0 }, 1), true);
+
+ // dimension mismatch
+ assertTrue(checkAppendError(cm, 1.0, new double[] { 0.0, 1.0 }, 2.0));
+
+ // hole between time ranges
+ assertTrue(checkAppendError(cm, 10.0, new double[] { 0.0, 1.0, -2.0 }, 20.0));
+
+ // propagation direction mismatch
+ assertTrue(checkAppendError(cm, 1.0, new double[] { 0.0, 1.0, -2.0 }, 0.0));
+
+ // no errors
+ assertFalse(checkAppendError(cm, 1.0, new double[] { 0.0, 1.0, -2.0 }, 2.0));
+
+ }
+
+ private boolean checkAppendError(ContinuousOutputModel cm,
+ double t0, double[] y0, double t1)
+ throws DerivativeException {
+ try {
+ ContinuousOutputModel otherCm = new ContinuousOutputModel();
+ otherCm.handleStep(buildInterpolator(t0, y0, t1), true);
+ cm.append(otherCm);
+ } catch(IllegalArgumentException iae) {
+ return true; // there was an allowable error
+ }
+ return false; // no allowable error
+ }
+
+ private StepInterpolator buildInterpolator(double t0, double[] y0, double t1) {
+ DummyStepInterpolator interpolator = new DummyStepInterpolator(y0, new double[y0.length], t1 >= t0);
+ interpolator.storeTime(t0);
+ interpolator.shift();
+ interpolator.storeTime(t1);
+ return interpolator;
+ }
+
+ public void checkValue(double value, double reference) {
+ assertTrue(FastMath.abs(value - reference) < 1.0e-10);
+ }
+
+ @Override
+ public void setUp() {
+ pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ integ = new DormandPrince54Integrator(minStep, maxStep, 1.0e-8, 1.0e-8);
+ }
+
+ @Override
+ public void tearDown() {
+ pb = null;
+ integ = null;
+ }
+
+ TestProblem3 pb;
+ FirstOrderIntegrator integ;
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/FirstOrderConverterTest.java b/src/test/java/org/apache/commons/math/ode/FirstOrderConverterTest.java
new file mode 100644
index 0000000..819e1b0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/FirstOrderConverterTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderConverter;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.SecondOrderDifferentialEquations;
+import org.apache.commons.math.ode.nonstiff.ClassicalRungeKuttaIntegrator;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class FirstOrderConverterTest
+ extends TestCase {
+
+ public FirstOrderConverterTest(String name) {
+ super(name);
+ }
+
+ public void testDoubleDimension() {
+ for (int i = 1; i < 10; ++i) {
+ SecondOrderDifferentialEquations eqn2 = new Equations(i, 0.2);
+ FirstOrderConverter eqn1 = new FirstOrderConverter(eqn2);
+ assertTrue(eqn1.getDimension() == (2 * eqn2.getDimension()));
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ double previousError = Double.NaN;
+ for (int i = 0; i < 10; ++i) {
+
+ double step = FastMath.pow(2.0, -(i + 1));
+ double error = integrateWithSpecifiedStep(4.0, 0.0, 1.0, step)
+ - FastMath.sin(4.0);
+ if (i > 0) {
+ assertTrue(FastMath.abs(error) < FastMath.abs(previousError));
+ }
+ previousError = error;
+
+ }
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+ double error = integrateWithSpecifiedStep(4.0, 0.0, 1.0, 1.0e-4)
+ - FastMath.sin(4.0);
+ assertTrue(FastMath.abs(error) < 1.0e-10);
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+ double error = integrateWithSpecifiedStep(4.0, 0.0, 1.0, 0.5)
+ - FastMath.sin(4.0);
+ assertTrue(FastMath.abs(error) > 0.1);
+ }
+
+ private static class Equations
+ implements SecondOrderDifferentialEquations {
+
+ private int n;
+
+ private double omega2;
+
+ public Equations(int n, double omega) {
+ this.n = n;
+ omega2 = omega * omega;
+ }
+
+ public int getDimension() {
+ return n;
+ }
+
+ public void computeSecondDerivatives(double t, double[] y, double[] yDot,
+ double[] yDDot) {
+ for (int i = 0; i < n; ++i) {
+ yDDot[i] = -omega2 * y[i];
+ }
+ }
+
+ }
+
+ private double integrateWithSpecifiedStep(double omega,
+ double t0, double t,
+ double step)
+ throws DerivativeException, IntegratorException {
+ double[] y0 = new double[2];
+ y0[0] = FastMath.sin(omega * t0);
+ y0[1] = omega * FastMath.cos(omega * t0);
+ ClassicalRungeKuttaIntegrator i = new ClassicalRungeKuttaIntegrator(step);
+ double[] y = new double[2];
+ i.integrate(new FirstOrderConverter(new Equations(1, omega)), t0, y0, t, y);
+ return y[0];
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem1.java b/src/test/java/org/apache/commons/math/ode/TestProblem1.java
new file mode 100644
index 0000000..fc8a381
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem1.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;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+
+ * <p>This specific problem is the following differential equation :
+ * <pre>
+ * y' = -y
+ * </pre>
+ * the solution of this equation is a simple exponential function :
+ * <pre>
+ * y (t) = y (t0) exp (t0-t)
+ * </pre>
+ * </p>
+
+ */
+public class TestProblem1
+ extends TestProblemAbstract {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1977870815289373164L;
+
+ /** theoretical state */
+ private double[] y;
+
+ /**
+ * Simple constructor.
+ */
+ public TestProblem1() {
+ super();
+ double[] y0 = { 1.0, 0.1 };
+ setInitialConditions(0.0, y0);
+ setFinalConditions(4.0);
+ double[] errorScale = { 1.0, 1.0 };
+ setErrorScale(errorScale);
+ y = new double[y0.length];
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ public TestProblem1(TestProblem1 problem) {
+ super(problem);
+ y = problem.y.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+public TestProblem1 copy() {
+ return new TestProblem1(this);
+ }
+
+ @Override
+ public void doComputeDerivatives(double t, double[] y, double[] yDot) {
+
+ // compute the derivatives
+ for (int i = 0; i < n; ++i)
+ yDot[i] = -y[i];
+
+ }
+
+ @Override
+ public double[] computeTheoreticalState(double t) {
+ double c = FastMath.exp (t0 - t);
+ for (int i = 0; i < n; ++i) {
+ y[i] = c * y0[i];
+ }
+ return y;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem2.java b/src/test/java/org/apache/commons/math/ode/TestProblem2.java
new file mode 100644
index 0000000..c506005
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem2.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.ode;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+
+ * <p>This specific problem is the following differential equation :
+ * <pre>
+ * y' = t^3 - t y
+ * </pre>
+ * with the initial condition y (0) = 0. The solution of this equation
+ * is the following function :
+ * <pre>
+ * y (t) = t^2 + 2 (exp (- t^2 / 2) - 1)
+ * </pre>
+ * </p>
+
+ */
+public class TestProblem2
+ extends TestProblemAbstract {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8330741783213512366L;
+
+ /** theoretical state */
+ private double[] y;
+
+ /**
+ * Simple constructor.
+ */
+ public TestProblem2() {
+ super();
+ double[] y0 = { 0.0 };
+ setInitialConditions(0.0, y0);
+ setFinalConditions(1.0);
+ double[] errorScale = { 1.0 };
+ setErrorScale(errorScale);
+ y = new double[y0.length];
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ public TestProblem2(TestProblem2 problem) {
+ super(problem);
+ y = problem.y.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+public TestProblem2 copy() {
+ return new TestProblem2(this);
+ }
+
+ @Override
+ public void doComputeDerivatives(double t, double[] y, double[] yDot) {
+
+ // compute the derivatives
+ for (int i = 0; i < n; ++i)
+ yDot[i] = t * (t * t - y[i]);
+
+ }
+
+ @Override
+ public double[] computeTheoreticalState(double t) {
+ double t2 = t * t;
+ double c = t2 + 2 * (FastMath.exp (-0.5 * t2) - 1);
+ for (int i = 0; i < n; ++i) {
+ y[i] = c;
+ }
+ return y;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem3.java b/src/test/java/org/apache/commons/math/ode/TestProblem3.java
new file mode 100644
index 0000000..cebdf9d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem3.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;
+
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+
+ * <p>This specific problem is the following differential equation :
+ * <pre>
+ * y1'' = -y1/r^3 y1 (0) = 1-e y1' (0) = 0
+ * y2'' = -y2/r^3 y2 (0) = 0 y2' (0) =sqrt((1+e)/(1-e))
+ * r = sqrt (y1^2 + y2^2), e = 0.9
+ * </pre>
+ * This is a two-body problem in the plane which can be solved by
+ * Kepler's equation
+ * <pre>
+ * y1 (t) = ...
+ * </pre>
+ * </p>
+
+ */
+public class TestProblem3
+ extends TestProblemAbstract {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8567328542728919999L;
+
+ /** Eccentricity */
+ double e;
+
+ /** theoretical state */
+ private double[] y;
+
+ /**
+ * Simple constructor.
+ * @param e eccentricity
+ */
+ public TestProblem3(double e) {
+ super();
+ this.e = e;
+ double[] y0 = { 1 - e, 0, 0, FastMath.sqrt((1+e)/(1-e)) };
+ setInitialConditions(0.0, y0);
+ setFinalConditions(20.0);
+ double[] errorScale = { 1.0, 1.0, 1.0, 1.0 };
+ setErrorScale(errorScale);
+ y = new double[y0.length];
+ }
+
+ /**
+ * Simple constructor.
+ */
+ public TestProblem3() {
+ this(0.1);
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ public TestProblem3(TestProblem3 problem) {
+ super(problem);
+ e = problem.e;
+ y = problem.y.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+public TestProblem3 copy() {
+ return new TestProblem3(this);
+ }
+
+ @Override
+ public void doComputeDerivatives(double t, double[] y, double[] yDot) {
+
+ // current radius
+ double r2 = y[0] * y[0] + y[1] * y[1];
+ double invR3 = 1 / (r2 * FastMath.sqrt(r2));
+
+ // compute the derivatives
+ yDot[0] = y[2];
+ yDot[1] = y[3];
+ yDot[2] = -invR3 * y[0];
+ yDot[3] = -invR3 * y[1];
+
+ }
+
+ @Override
+ public double[] computeTheoreticalState(double t) {
+
+ // solve Kepler's equation
+ double E = t;
+ double d = 0;
+ double corr = 999.0;
+ for (int i = 0; (i < 50) && (FastMath.abs(corr) > 1.0e-12); ++i) {
+ double f2 = e * FastMath.sin(E);
+ double f0 = d - f2;
+ double f1 = 1 - e * FastMath.cos(E);
+ double f12 = f1 + f1;
+ corr = f0 * f12 / (f1 * f12 - f0 * f2);
+ d -= corr;
+ E = t + d;
+ }
+
+ double cosE = FastMath.cos(E);
+ double sinE = FastMath.sin(E);
+
+ y[0] = cosE - e;
+ y[1] = FastMath.sqrt(1 - e * e) * sinE;
+ y[2] = -sinE / (1 - e * cosE);
+ y[3] = FastMath.sqrt(1 - e * e) * cosE / (1 - e * cosE);
+
+ return y;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem4.java b/src/test/java/org/apache/commons/math/ode/TestProblem4.java
new file mode 100644
index 0000000..b2f5e56
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem4.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.ode;
+
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+
+ * <p>This specific problem is the following differential equation :
+ * <pre>
+ * x'' = -x
+ * </pre>
+ * And when x decreases down to 0, the state should be changed as follows :
+ * <pre>
+ * x' -> -x'
+ * </pre>
+ * The theoretical solution of this problem is x = |sin(t+a)|
+ * </p>
+
+ */
+public class TestProblem4
+ extends TestProblemAbstract {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5910438521889015745L;
+
+ /** Time offset. */
+ private double a;
+
+ /** theoretical state */
+ private double[] y;
+
+ /** Simple constructor. */
+ public TestProblem4() {
+ super();
+ a = 1.2;
+ double[] y0 = { FastMath.sin(a), FastMath.cos(a) };
+ setInitialConditions(0.0, y0);
+ setFinalConditions(15);
+ double[] errorScale = { 1.0, 0.0 };
+ setErrorScale(errorScale);
+ y = new double[y0.length];
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ public TestProblem4(TestProblem4 problem) {
+ super(problem);
+ a = problem.a;
+ y = problem.y.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+public TestProblem4 copy() {
+ return new TestProblem4(this);
+ }
+
+ @Override
+ public EventHandler[] getEventsHandlers() {
+ return new EventHandler[] { new Bounce(), new Stop() };
+ }
+
+ /**
+ * Get the theoretical events times.
+ * @return theoretical events times
+ */
+ @Override
+ public double[] getTheoreticalEventsTimes() {
+ return new double[] {
+ 1 * FastMath.PI - a,
+ 2 * FastMath.PI - a,
+ 3 * FastMath.PI - a,
+ 4 * FastMath.PI - a,
+ 12.0
+ };
+ }
+
+ @Override
+ public void doComputeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = y[1];
+ yDot[1] = -y[0];
+ }
+
+ @Override
+ public double[] computeTheoreticalState(double t) {
+ double sin = FastMath.sin(t + a);
+ double cos = FastMath.cos(t + a);
+ y[0] = FastMath.abs(sin);
+ y[1] = (sin >= 0) ? cos : -cos;
+ return y;
+ }
+
+ private static class Bounce implements EventHandler {
+
+ private static final long serialVersionUID = 1356097180027801200L;
+ private int sign;
+
+ public Bounce() {
+ sign = +1;
+ }
+
+ public double g(double t, double[] y) {
+ return sign * y[0];
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ // this sign change is needed because the state will be reset soon
+ sign = -sign;
+ return EventHandler.RESET_STATE;
+ }
+
+ public void resetState(double t, double[] y) {
+ y[0] = -y[0];
+ y[1] = -y[1];
+ }
+
+ }
+
+ private static class Stop implements EventHandler {
+
+ private static final long serialVersionUID = 6975050568227951931L;
+
+ public Stop() {
+ }
+
+ public double g(double t, double[] y) {
+ return t - 12.0;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ return EventHandler.STOP;
+ }
+
+ public void resetState(double t, double[] y) {
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem5.java b/src/test/java/org/apache/commons/math/ode/TestProblem5.java
new file mode 100644
index 0000000..8a53941
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem5.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.ode;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+ * <p>This is the same as problem 1 except integration is done
+ * backward in time</p>
+ */
+public class TestProblem5
+ extends TestProblem1 {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 7579233102411804237L;
+
+ /**
+ * Simple constructor.
+ */
+ public TestProblem5() {
+ super();
+ setFinalConditions(2 * t0 - t1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TestProblem5 copy() {
+ return new TestProblem5();
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblem6.java b/src/test/java/org/apache/commons/math/ode/TestProblem6.java
new file mode 100644
index 0000000..32fcfda
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblem6.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.ode;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+
+ * <p>This specific problem is the following differential equation :
+ * <pre>
+ * y' = 3x^5 - y
+ * </pre>
+ * when the initial condition is y(0) = -360, the solution of this
+ * equation degenerates to a simple quintic polynomial function :
+ * <pre>
+ * y (t) = 3x^5 - 15x^4 + 60x^3 - 180x^2 + 360x - 360
+ * </pre>
+ * </p>
+
+ */
+public class TestProblem6
+ extends TestProblemAbstract {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1353409119804352378L;
+
+ /** theoretical state */
+ private double[] y;
+
+ /**
+ * Simple constructor.
+ */
+ public TestProblem6() {
+ super();
+ double[] y0 = { -360.0 };
+ setInitialConditions(0.0, y0);
+ setFinalConditions(1.0);
+ double[] errorScale = { 1.0 };
+ setErrorScale(errorScale);
+ y = new double[y0.length];
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ public TestProblem6(TestProblem6 problem) {
+ super(problem);
+ y = problem.y.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TestProblem6 copy() {
+ return new TestProblem6(this);
+ }
+
+ @Override
+ public void doComputeDerivatives(double t, double[] y, double[] yDot) {
+
+ // compute the derivatives
+ double t2 = t * t;
+ double t4 = t2 * t2;
+ double t5 = t4 * t;
+ for (int i = 0; i < n; ++i) {
+ yDot[i] = 3 * t5 - y[i];
+ }
+
+ }
+
+ @Override
+ public double[] computeTheoreticalState(double t) {
+ for (int i = 0; i < n; ++i) {
+ y[i] = ((((3 * t - 15) * t + 60) * t - 180) * t + 360) * t - 360;
+ }
+ return y;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblemAbstract.java b/src/test/java/org/apache/commons/math/ode/TestProblemAbstract.java
new file mode 100644
index 0000000..f6492ce
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblemAbstract.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode;
+
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.events.EventHandler;
+
+/**
+ * This class is used as the base class of the problems that are
+ * integrated during the junit tests for the ODE integrators.
+ */
+public abstract class TestProblemAbstract
+ implements FirstOrderDifferentialEquations {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -8521928974502839379L;
+
+ /** Dimension of the problem. */
+ protected int n;
+
+ /** Number of functions calls. */
+ protected int calls;
+
+ /** Initial time */
+ protected double t0;
+
+ /** Initial state */
+ protected double[] y0;
+
+ /** Final time */
+ protected double t1;
+
+ /** Error scale */
+ protected double[] errorScale;
+
+ /**
+ * Simple constructor.
+ */
+ protected TestProblemAbstract() {
+ n = 0;
+ calls = 0;
+ t0 = 0;
+ y0 = null;
+ t1 = 0;
+ errorScale = null;
+ }
+
+ /**
+ * Copy constructor.
+ * @param problem problem to copy
+ */
+ protected TestProblemAbstract(TestProblemAbstract problem) {
+ n = problem.n;
+ calls = problem.calls;
+ t0 = problem.t0;
+ if (problem.y0 == null) {
+ y0 = null;
+ } else {
+ y0 = problem.y0.clone();
+ }
+ if (problem.errorScale == null) {
+ errorScale = null;
+ } else {
+ errorScale = problem.errorScale.clone();
+ }
+ t1 = problem.t1;
+ }
+
+ /**
+ * Copy operation.
+ * @return a copy of the instance
+ */
+ public abstract TestProblemAbstract copy();
+
+ /**
+ * Set the initial conditions
+ * @param t0 initial time
+ * @param y0 initial state vector
+ */
+ protected void setInitialConditions(double t0, double[] y0) {
+ calls = 0;
+ n = y0.length;
+ this.t0 = t0;
+ this.y0 = y0.clone();
+ }
+
+ /**
+ * Set the final conditions.
+ * @param t1 final time
+ */
+ protected void setFinalConditions(double t1) {
+ this.t1 = t1;
+ }
+
+ /**
+ * Set the error scale
+ * @param errorScale error scale
+ */
+ protected void setErrorScale(double[] errorScale) {
+ this.errorScale = errorScale.clone();
+ }
+
+ public int getDimension() {
+ return n;
+ }
+
+ /**
+ * Get the initial time.
+ * @return initial time
+ */
+ public double getInitialTime() {
+ return t0;
+ }
+
+ /**
+ * Get the initial state vector.
+ * @return initial state vector
+ */
+ public double[] getInitialState() {
+ return y0;
+ }
+
+ /**
+ * Get the final time.
+ * @return final time
+ */
+ public double getFinalTime() {
+ return t1;
+ }
+
+ /**
+ * Get the error scale.
+ * @return error scale
+ */
+ public double[] getErrorScale() {
+ return errorScale;
+ }
+
+ /**
+ * Get the events handlers.
+ * @return events handlers */
+ public EventHandler[] getEventsHandlers() {
+ return new EventHandler[0];
+ }
+
+ /**
+ * Get the theoretical events times.
+ * @return theoretical events times
+ */
+ public double[] getTheoreticalEventsTimes() {
+ return new double[0];
+ }
+
+ /**
+ * Get the number of calls.
+ * @return nuber of calls
+ */
+ public int getCalls() {
+ return calls;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ ++calls;
+ doComputeDerivatives(t, y, yDot);
+ }
+
+ abstract public void doComputeDerivatives(double t, double[] y, double[] yDot);
+
+ /**
+ * Compute the theoretical state at the specified time.
+ * @param t time at which the state is required
+ * @return state vector at time t
+ */
+ abstract public double[] computeTheoreticalState(double t);
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblemFactory.java b/src/test/java/org/apache/commons/math/ode/TestProblemFactory.java
new file mode 100644
index 0000000..c9584c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblemFactory.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.ode;
+
+/**
+ * This class is used in the junit tests for the ODE integrators.
+ */
+public class TestProblemFactory {
+
+ /** Problems pool. */
+ private static final TestProblemAbstract[] pool = {
+ new TestProblem1(),
+ new TestProblem2(),
+ new TestProblem3(),
+ new TestProblem4(),
+ new TestProblem5(),
+ new TestProblem6()
+ };
+
+ /**
+ * Private constructor.
+ * This is a utility class, so there are no instance at all.
+ */
+ private TestProblemFactory() {
+ }
+
+ /**
+ * Get the problems.
+ * @return array of problems to solve
+ */
+ public static TestProblemAbstract[] getProblems() {
+ return pool;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/TestProblemHandler.java b/src/test/java/org/apache/commons/math/ode/TestProblemHandler.java
new file mode 100644
index 0000000..0618b89
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/TestProblemHandler.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ODEIntegrator;
+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 used to handle steps for the test problems
+ * integrated during the junit tests for the ODE integrators.
+ */
+public class TestProblemHandler
+ implements StepHandler {
+
+ /** Associated problem. */
+ private TestProblemAbstract problem;
+
+ /** Maximal errors encountered during the integration. */
+ private double maxValueError;
+ private double maxTimeError;
+
+ /** Error at the end of the integration. */
+ private double lastError;
+
+ /** Time at the end of integration. */
+ private double lastTime;
+
+ /** ODE solver used. */
+ private ODEIntegrator integrator;
+
+ /** Expected start for step. */
+ private double expectedStepStart;
+
+ /**
+ * Simple constructor.
+ * @param problem problem for which steps should be handled
+ * @param integrator ODE solver used
+ */
+ public TestProblemHandler(TestProblemAbstract problem, ODEIntegrator integrator) {
+ this.problem = problem;
+ this.integrator = integrator;
+ reset();
+ }
+
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+
+ public void reset() {
+ maxValueError = 0;
+ maxTimeError = 0;
+ lastError = 0;
+ expectedStepStart = Double.NaN;
+ }
+
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast)
+ throws DerivativeException {
+
+ double start = integrator.getCurrentStepStart();
+ if (FastMath.abs((start - problem.getInitialTime()) / integrator.getCurrentSignedStepsize()) > 0.001) {
+ // multistep integrators do not handle the first steps themselves
+ // so we have to make sure the integrator we look at has really started its work
+ if (!Double.isNaN(expectedStepStart)) {
+ // the step should either start at the end of the integrator step
+ // or at an event if the step is split into several substeps
+ double stepError = FastMath.max(maxTimeError, FastMath.abs(start - expectedStepStart));
+ for (double eventTime : problem.getTheoreticalEventsTimes()) {
+ stepError = FastMath.min(stepError, FastMath.abs(start - eventTime));
+ }
+ maxTimeError = FastMath.max(maxTimeError, stepError);
+ }
+ expectedStepStart = start + integrator.getCurrentSignedStepsize();
+ }
+
+ double pT = interpolator.getPreviousTime();
+ double cT = interpolator.getCurrentTime();
+ double[] errorScale = problem.getErrorScale();
+
+ // store the error at the last step
+ if (isLast) {
+ double[] interpolatedY = interpolator.getInterpolatedState();
+ double[] theoreticalY = problem.computeTheoreticalState(cT);
+ for (int i = 0; i < interpolatedY.length; ++i) {
+ double error = FastMath.abs(interpolatedY[i] - theoreticalY[i]);
+ lastError = FastMath.max(error, lastError);
+ }
+ lastTime = cT;
+ }
+
+ // walk through the step
+ for (int k = 0; k <= 20; ++k) {
+
+ double time = pT + (k * (cT - pT)) / 20;
+ interpolator.setInterpolatedTime(time);
+ double[] interpolatedY = interpolator.getInterpolatedState();
+ double[] theoreticalY = problem.computeTheoreticalState(interpolator.getInterpolatedTime());
+
+ // update the errors
+ for (int i = 0; i < interpolatedY.length; ++i) {
+ double error = errorScale[i] * FastMath.abs(interpolatedY[i] - theoreticalY[i]);
+ maxValueError = FastMath.max(error, maxValueError);
+ }
+ }
+ }
+
+ /**
+ * Get the maximal value error encountered during integration.
+ * @return maximal value error
+ */
+ public double getMaximalValueError() {
+ return maxValueError;
+ }
+
+ /**
+ * Get the maximal time error encountered during integration.
+ * @return maximal time error
+ */
+ public double getMaximalTimeError() {
+ return maxTimeError;
+ }
+
+ /**
+ * Get the error at the end of the integration.
+ * @return error at the end of the integration
+ */
+ public double getLastError() {
+ return lastError;
+ }
+
+ /**
+ * Get the time at the end of the integration.
+ * @return time at the end of the integration.
+ */
+ public double getLastTime() {
+ return lastTime;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java b/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java
new file mode 100644
index 0000000..dff6d6b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/events/EventStateTest.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.ode.events;
+
+import junit.framework.Assert;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.junit.Test;
+
+public class EventStateTest {
+
+ // JIRA: MATH-322
+ @Test
+ public void closeEvents()
+ throws EventException, ConvergenceException, DerivativeException {
+
+ final double r1 = 90.0;
+ final double r2 = 135.0;
+ final double gap = r2 - r1;
+ EventHandler closeEventsGenerator = new EventHandler() {
+ public void resetState(double t, double[] y) {
+ }
+ public double g(double t, double[] y) {
+ return (t - r1) * (r2 - t);
+ }
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ return CONTINUE;
+ }
+ };
+
+ final double tolerance = 0.1;
+ EventState es = new EventState(closeEventsGenerator, 1.5 * gap, tolerance, 10);
+
+ AbstractStepInterpolator interpolator =
+ new DummyStepInterpolator(new double[0], new double[0], true);
+ interpolator.storeTime(r1 - 2.5 * gap);
+ interpolator.shift();
+ interpolator.storeTime(r1 - 1.5 * gap);
+ es.reinitializeBegin(interpolator);
+
+ interpolator.shift();
+ interpolator.storeTime(r1 - 0.5 * gap);
+ Assert.assertFalse(es.evaluateStep(interpolator));
+
+ interpolator.shift();
+ interpolator.storeTime(0.5 * (r1 + r2));
+ Assert.assertTrue(es.evaluateStep(interpolator));
+ Assert.assertEquals(r1, es.getEventTime(), tolerance);
+ es.stepAccepted(es.getEventTime(), new double[0]);
+
+ interpolator.shift();
+ interpolator.storeTime(r2 + 0.4 * gap);
+ Assert.assertTrue(es.evaluateStep(interpolator));
+ Assert.assertEquals(r2, es.getEventTime(), tolerance);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobiansTest.java b/src/test/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobiansTest.java
new file mode 100644
index 0000000..0a7ed7a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/jacobians/FirstOrderIntegratorWithJacobiansTest.java
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+ at Deprecated
+public class FirstOrderIntegratorWithJacobiansTest {
+
+ @Test
+ public void testLowAccuracyExternalDifferentiation()
+ throws IntegratorException, DerivativeException {
+ // this test does not really test FirstOrderIntegratorWithJacobians,
+ // it only shows that WITHOUT this class, attempting to recover
+ // the jacobians from external differentiation on simple integration
+ // results with low accuracy gives very poor results. In fact,
+ // the curves dy/dp = g(b) when b varies from 2.88 to 3.08 are
+ // essentially noise.
+ // This test is taken from Hairer, Norsett and Wanner book
+ // Solving Ordinary Differential Equations I (Nonstiff problems),
+ // the curves dy/dp = g(b) are in figure 6.5
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 });
+ double hP = 1.0e-12;
+ SummaryStatistics residualsP0 = new SummaryStatistics();
+ SummaryStatistics residualsP1 = new SummaryStatistics();
+ for (double b = 2.88; b < 3.08; b += 0.001) {
+ Brusselator brusselator = new Brusselator(b);
+ double[] y = { 1.3, b };
+ integ.integrate(brusselator, 0, y, 20.0, y);
+ double[] yP = { 1.3, b + hP };
+ brusselator.setParameter(0, b + hP);
+ integ.integrate(brusselator, 0, yP, 20.0, yP);
+ residualsP0.addValue((yP[0] - y[0]) / hP - brusselator.dYdP0());
+ residualsP1.addValue((yP[1] - y[1]) / hP - brusselator.dYdP1());
+ }
+ Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) > 600);
+ Assert.assertTrue(residualsP0.getStandardDeviation() > 30);
+ Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) > 800);
+ Assert.assertTrue(residualsP1.getStandardDeviation() > 50);
+ }
+
+ @Test
+ public void testHighAccuracyExternalDifferentiation()
+ throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 });
+ double hP = 1.0e-12;
+ SummaryStatistics residualsP0 = new SummaryStatistics();
+ SummaryStatistics residualsP1 = new SummaryStatistics();
+ for (double b = 2.88; b < 3.08; b += 0.001) {
+ Brusselator brusselator = new Brusselator(b);
+ double[] y = { 1.3, b };
+ integ.integrate(brusselator, 0, y, 20.0, y);
+ double[] yP = { 1.3, b + hP };
+ brusselator.setParameter(0, b + hP);
+ integ.integrate(brusselator, 0, yP, 20.0, yP);
+ residualsP0.addValue((yP[0] - y[0]) / hP - brusselator.dYdP0());
+ residualsP1.addValue((yP[1] - y[1]) / hP - brusselator.dYdP1());
+ }
+ Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) > 0.02);
+ Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.03);
+ Assert.assertTrue(residualsP0.getStandardDeviation() > 0.003);
+ Assert.assertTrue(residualsP0.getStandardDeviation() < 0.004);
+ Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) > 0.04);
+ Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05);
+ Assert.assertTrue(residualsP1.getStandardDeviation() > 0.007);
+ Assert.assertTrue(residualsP1.getStandardDeviation() < 0.008);
+ }
+
+ @Test
+ public void testInternalDifferentiation()
+ throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 });
+ double hP = 1.0e-12;
+ SummaryStatistics residualsP0 = new SummaryStatistics();
+ SummaryStatistics residualsP1 = new SummaryStatistics();
+ for (double b = 2.88; b < 3.08; b += 0.001) {
+ Brusselator brusselator = new Brusselator(b);
+ brusselator.setParameter(0, b);
+ double[] z = { 1.3, b };
+ double[][] dZdZ0 = new double[2][2];
+ double[][] dZdP = new double[2][1];
+ double hY = 1.0e-12;
+ FirstOrderIntegratorWithJacobians extInt =
+ new FirstOrderIntegratorWithJacobians(integ, brusselator, new double[] { b },
+ new double[] { hY, hY }, new double[] { hP });
+ extInt.setMaxEvaluations(5000);
+ extInt.integrate(0, z, new double[][] { { 0.0 }, { 1.0 } }, 20.0, z, dZdZ0, dZdP);
+ Assert.assertEquals(5000, extInt.getMaxEvaluations());
+ Assert.assertTrue(extInt.getEvaluations() > 1400);
+ Assert.assertTrue(extInt.getEvaluations() < 2000);
+ Assert.assertEquals(4 * integ.getEvaluations(), extInt.getEvaluations());
+ residualsP0.addValue(dZdP[0][0] - brusselator.dYdP0());
+ residualsP1.addValue(dZdP[1][0] - brusselator.dYdP1());
+ }
+ Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.02);
+ Assert.assertTrue(residualsP0.getStandardDeviation() < 0.003);
+ Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05);
+ Assert.assertTrue(residualsP1.getStandardDeviation() < 0.01);
+ }
+
+ @Test
+ public void testAnalyticalDifferentiation()
+ throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 });
+ SummaryStatistics residualsP0 = new SummaryStatistics();
+ SummaryStatistics residualsP1 = new SummaryStatistics();
+ for (double b = 2.88; b < 3.08; b += 0.001) {
+ Brusselator brusselator = new Brusselator(b);
+ brusselator.setParameter(0, b);
+ double[] z = { 1.3, b };
+ double[][] dZdZ0 = new double[2][2];
+ double[][] dZdP = new double[2][1];
+ FirstOrderIntegratorWithJacobians extInt =
+ new FirstOrderIntegratorWithJacobians(integ, brusselator);
+ extInt.setMaxEvaluations(5000);
+ extInt.integrate(0, z, new double[][] { { 0.0 }, { 1.0 } }, 20.0, z, dZdZ0, dZdP);
+ Assert.assertEquals(5000, extInt.getMaxEvaluations());
+ Assert.assertTrue(extInt.getEvaluations() > 350);
+ Assert.assertTrue(extInt.getEvaluations() < 510);
+ Assert.assertEquals(integ.getEvaluations(), extInt.getEvaluations());
+ residualsP0.addValue(dZdP[0][0] - brusselator.dYdP0());
+ residualsP1.addValue(dZdP[1][0] - brusselator.dYdP1());
+ }
+ Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.014);
+ Assert.assertTrue(residualsP0.getStandardDeviation() < 0.003);
+ Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05);
+ Assert.assertTrue(residualsP1.getStandardDeviation() < 0.01);
+ }
+
+ @Test
+ public void testFinalResult() throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 });
+ double[] y = new double[] { 0.0, 1.0 };
+ Circle circle = new Circle(y, 1.0, 1.0, 0.1);
+ double[][] dydy0 = new double[2][2];
+ double[][] dydp = new double[2][3];
+ double t = 18 * FastMath.PI;
+ FirstOrderIntegratorWithJacobians extInt =
+ new FirstOrderIntegratorWithJacobians(integ, circle);
+ extInt.integrate(0, y, circle.exactDyDp(0), t, y, dydy0, dydp);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(circle.exactY(t)[i], y[i], 1.0e-9);
+ }
+ for (int i = 0; i < dydy0.length; ++i) {
+ for (int j = 0; j < dydy0[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDy0(t)[i][j], dydy0[i][j], 1.0e-9);
+ }
+ }
+ for (int i = 0; i < dydp.length; ++i) {
+ for (int j = 0; j < dydp[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDp(t)[i][j], dydp[i][j], 1.0e-7);
+ }
+ }
+ }
+
+ @Test
+ public void testStepHandlerResult() throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 });
+ double[] y = new double[] { 0.0, 1.0 };
+ final Circle circle = new Circle(y, 1.0, 1.0, 0.1);
+ double[][] dydy0 = new double[2][2];
+ double[][] dydp = new double[2][3];
+ double t = 18 * FastMath.PI;
+ final FirstOrderIntegratorWithJacobians extInt =
+ new FirstOrderIntegratorWithJacobians(integ, circle);
+ extInt.addStepHandler(new StepHandlerWithJacobians() {
+
+ public void reset() {
+ }
+
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+
+ public void handleStep(StepInterpolatorWithJacobians interpolator, boolean isLast)
+ throws DerivativeException {
+ double t = interpolator.getCurrentTime();
+ double[] y = interpolator.getInterpolatedY();
+ double[][] dydy0 = interpolator.getInterpolatedDyDy0();
+ double[][] dydp = interpolator.getInterpolatedDyDp();
+ Assert.assertEquals(interpolator.getPreviousTime(), extInt.getCurrentStepStart(), 1.0e-10);
+ Assert.assertTrue(extInt.getCurrentSignedStepsize() < 0.5);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(circle.exactY(t)[i], y[i], 1.0e-9);
+ }
+ for (int i = 0; i < dydy0.length; ++i) {
+ for (int j = 0; j < dydy0[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDy0(t)[i][j], dydy0[i][j], 1.0e-9);
+ }
+ }
+ for (int i = 0; i < dydp.length; ++i) {
+ for (int j = 0; j < dydp[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDp(t)[i][j], dydp[i][j], 3.0e-8);
+ }
+ }
+
+ double[] yDot = interpolator.getInterpolatedYDot();
+ double[][] dydy0Dot = interpolator.getInterpolatedDyDy0Dot();
+ double[][] dydpDot = interpolator.getInterpolatedDyDpDot();
+
+ for (int i = 0; i < yDot.length; ++i) {
+ Assert.assertEquals(circle.exactYDot(t)[i], yDot[i], 1.0e-10);
+ }
+ for (int i = 0; i < dydy0Dot.length; ++i) {
+ for (int j = 0; j < dydy0Dot[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDy0Dot(t)[i][j], dydy0Dot[i][j], 1.0e-10);
+ }
+ }
+ for (int i = 0; i < dydpDot.length; ++i) {
+ for (int j = 0; j < dydpDot[i].length; ++j) {
+ Assert.assertEquals(circle.exactDyDpDot(t)[i][j], dydpDot[i][j], 3.0e-9);
+ }
+ }
+ }
+ });
+ extInt.integrate(0, y, circle.exactDyDp(0), t, y, dydy0, dydp);
+ }
+
+ @Test
+ public void testEventHandler() throws IntegratorException, DerivativeException {
+ FirstOrderIntegrator integ =
+ new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 });
+ double[] y = new double[] { 0.0, 1.0 };
+ final Circle circle = new Circle(y, 1.0, 1.0, 0.1);
+ double[][] dydy0 = new double[2][2];
+ double[][] dydp = new double[2][3];
+ double t = 18 * FastMath.PI;
+ final FirstOrderIntegratorWithJacobians extInt =
+ new FirstOrderIntegratorWithJacobians(integ, circle);
+ extInt.addEventHandler(new EventHandlerWithJacobians() {
+
+ public int eventOccurred(double t, double[] y, double[][] dydy0,
+ double[][] dydp, boolean increasing) {
+ Assert.assertEquals(0.1, y[1], 1.0e-11);
+ Assert.assertTrue(!increasing);
+ return STOP;
+ }
+
+ public double g(double t, double[] y, double[][] dydy0,
+ double[][] dydp) {
+ return y[1] - 0.1;
+ }
+
+ public void resetState(double t, double[] y, double[][] dydy0,
+ double[][] dydp) {
+ }
+ }, 10.0, 1.0e-10, 1000);
+ double stopTime = extInt.integrate(0, y, circle.exactDyDp(0), t, y, dydy0, dydp);
+ Assert.assertTrue(stopTime < 5.0 * FastMath.PI);
+ }
+
+ private static class Brusselator implements ParameterizedODE, ODEWithJacobians {
+
+ private double b;
+
+ public Brusselator(double b) {
+ this.b = b;
+ }
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public void setParameter(int i, double p) {
+ b = p;
+ }
+
+ public int getParametersDimension() {
+ return 1;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ double prod = y[0] * y[0] * y[1];
+ yDot[0] = 1 + prod - (b + 1) * y[0];
+ yDot[1] = b * y[0] - prod;
+ }
+
+ public void computeJacobians(double t, double[] y, double[] yDot, double[][] dFdY, double[][] dFdP) {
+ double p = 2 * y[0] * y[1];
+ double y02 = y[0] * y[0];
+ dFdY[0][0] = p - (1 + b);
+ dFdY[0][1] = y02;
+ dFdY[1][0] = b - p;
+ dFdY[1][1] = -y02;
+ dFdP[0][0] = -y[0];
+ dFdP[1][0] = y[0];
+ }
+
+ public double dYdP0() {
+ return -1088.232716447743 + (1050.775747149553 + (-339.012934631828 + 36.52917025056327 * b) * b) * b;
+ }
+
+ public double dYdP1() {
+ return 1502.824469929139 + (-1438.6974831849952 + (460.959476642384 - 49.43847385647082 * b) * b) * b;
+ }
+
+ }
+
+ /** ODE representing a point moving on a circle with provided center and angular rate. */
+ private static class Circle implements ODEWithJacobians {
+
+ private final double[] y0;
+ private double cx;
+ private double cy;
+ private double omega;
+
+ public Circle(double[] y0, double cx, double cy, double omega) {
+ this.y0 = y0.clone();
+ this.cx = cx;
+ this.cy = cy;
+ this.omega = omega;
+ }
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public int getParametersDimension() {
+ return 3;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = omega * (cy - y[1]);
+ yDot[1] = omega * (y[0] - cx);
+ }
+
+ public void computeJacobians(double t, double[] y, double[] yDot, double[][] dFdY, double[][] dFdP) {
+
+ dFdY[0][0] = 0;
+ dFdY[0][1] = -omega;
+ dFdY[1][0] = omega;
+ dFdY[1][1] = 0;
+
+ dFdP[0][0] = 0;
+ dFdP[0][1] = omega;
+ dFdP[0][2] = cy - y[1];
+ dFdP[1][0] = -omega;
+ dFdP[1][1] = 0;
+ dFdP[1][2] = y[0] - cx;
+
+ }
+
+ public double[] exactY(double t) {
+ double cos = FastMath.cos(omega * t);
+ double sin = FastMath.sin(omega * t);
+ double dx0 = y0[0] - cx;
+ double dy0 = y0[1] - cy;
+ return new double[] {
+ cx + cos * dx0 - sin * dy0,
+ cy + sin * dx0 + cos * dy0
+ };
+ }
+
+ public double[][] exactDyDy0(double t) {
+ double cos = FastMath.cos(omega * t);
+ double sin = FastMath.sin(omega * t);
+ return new double[][] {
+ { cos, -sin },
+ { sin, cos }
+ };
+ }
+
+ public double[][] exactDyDp(double t) {
+ double cos = FastMath.cos(omega * t);
+ double sin = FastMath.sin(omega * t);
+ double dx0 = y0[0] - cx;
+ double dy0 = y0[1] - cy;
+ return new double[][] {
+ { 1 - cos, sin, -t * (sin * dx0 + cos * dy0) },
+ { -sin, 1 - cos, t * (cos * dx0 - sin * dy0) }
+ };
+ }
+
+ public double[] exactYDot(double t) {
+ double oCos = omega * FastMath.cos(omega * t);
+ double oSin = omega * FastMath.sin(omega * t);
+ double dx0 = y0[0] - cx;
+ double dy0 = y0[1] - cy;
+ return new double[] {
+ -oSin * dx0 - oCos * dy0,
+ oCos * dx0 - oSin * dy0
+ };
+ }
+
+ public double[][] exactDyDy0Dot(double t) {
+ double oCos = omega * FastMath.cos(omega * t);
+ double oSin = omega * FastMath.sin(omega * t);
+ return new double[][] {
+ { -oSin, -oCos },
+ { oCos, -oSin }
+ };
+ }
+
+ public double[][] exactDyDpDot(double t) {
+ double cos = FastMath.cos(omega * t);
+ double sin = FastMath.sin(omega * t);
+ double oCos = omega * cos;
+ double oSin = omega * sin;
+ double dx0 = y0[0] - cx;
+ double dy0 = y0[1] - cy;
+ return new double[][] {
+ { oSin, oCos, -sin * dx0 - cos * dy0 - t * ( oCos * dx0 - oSin * dy0) },
+ { -oCos, oSin, cos * dx0 - sin * dy0 + t * (-oSin * dx0 - oCos * dy0) }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegratorTest.java
new file mode 100644
index 0000000..7eeac66
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegratorTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblem6;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class AdamsBashforthIntegratorTest {
+
+ @Test(expected=IntegratorException.class)
+ public void dimensionCheck() throws DerivativeException, IntegratorException {
+ TestProblem1 pb = new TestProblem1();
+ FirstOrderIntegrator integ =
+ new AdamsBashforthIntegrator(2, 0.0, 1.0, 1.0e-10, 1.0e-10);
+ integ.integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ }
+
+ @Test(expected=IntegratorException.class)
+ public void testMinStep() throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
+ double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
+
+ FirstOrderIntegrator integ = new AdamsBashforthIntegrator(4, minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+ @Test
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -5; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = FastMath.pow(10.0, i);
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new AdamsBashforthIntegrator(4, minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ // the 31 and 36 factors are only valid for this test
+ // and has been obtained from trial and error
+ // there is no general relation between local and global errors
+ assertTrue(handler.getMaximalValueError() > (31.0 * scalAbsoluteTolerance));
+ assertTrue(handler.getMaximalValueError() < (36.0 * scalAbsoluteTolerance));
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ @Test(expected = DerivativeException.class)
+ public void exceedMaxEvaluations() throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double range = pb.getFinalTime() - pb.getInitialTime();
+
+ AdamsBashforthIntegrator integ = new AdamsBashforthIntegrator(2, 0, range, 1.0e-12, 1.0e-12);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.setMaxEvaluations(650);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+ @Test
+ public void backward() throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double range = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+
+ FirstOrderIntegrator integ = new AdamsBashforthIntegrator(4, 0, range, 1.0e-12, 1.0e-12);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 1.0e-8);
+ assertTrue(handler.getMaximalValueError() < 1.0e-8);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
+ assertEquals("Adams-Bashforth", integ.getName());
+ }
+
+ @Test
+ public void polynomial() throws DerivativeException, IntegratorException {
+ TestProblem6 pb = new TestProblem6();
+ double range = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+
+ for (int nSteps = 1; nSteps < 8; ++nSteps) {
+ AdamsBashforthIntegrator integ =
+ new AdamsBashforthIntegrator(nSteps, 1.0e-6 * range, 0.1 * range, 1.0e-10, 1.0e-10);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (nSteps < 4) {
+ assertTrue(integ.getEvaluations() > 150);
+ } else {
+ assertTrue(integ.getEvaluations() < 70);
+ }
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegratorTest.java
new file mode 100644
index 0000000..4c9f711
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegratorTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblem6;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class AdamsMoultonIntegratorTest {
+
+ @Test(expected=IntegratorException.class)
+ public void dimensionCheck() throws DerivativeException, IntegratorException {
+ TestProblem1 pb = new TestProblem1();
+ FirstOrderIntegrator integ =
+ new AdamsMoultonIntegrator(2, 0.0, 1.0, 1.0e-10, 1.0e-10);
+ integ.integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ }
+
+ @Test(expected=IntegratorException.class)
+ public void testMinStep() throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
+ double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
+
+ FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+ @Test
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -2; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = FastMath.pow(10.0, i);
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ // the 0.15 and 3.0 factors are only valid for this test
+ // and has been obtained from trial and error
+ // there is no general relation between local and global errors
+ assertTrue(handler.getMaximalValueError() > (0.15 * scalAbsoluteTolerance));
+ assertTrue(handler.getMaximalValueError() < (3.0 * scalAbsoluteTolerance));
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ @Test(expected = DerivativeException.class)
+ public void exceedMaxEvaluations() throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double range = pb.getFinalTime() - pb.getInitialTime();
+
+ AdamsMoultonIntegrator integ = new AdamsMoultonIntegrator(2, 0, range, 1.0e-12, 1.0e-12);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.setMaxEvaluations(650);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+ @Test
+ public void backward() throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double range = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+
+ FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, 0, range, 1.0e-12, 1.0e-12);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 1.0e-9);
+ assertTrue(handler.getMaximalValueError() < 1.0e-9);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
+ assertEquals("Adams-Moulton", integ.getName());
+ }
+
+ @Test
+ public void polynomial() throws DerivativeException, IntegratorException {
+ TestProblem6 pb = new TestProblem6();
+ double range = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+
+ for (int nSteps = 1; nSteps < 7; ++nSteps) {
+ AdamsMoultonIntegrator integ =
+ new AdamsMoultonIntegrator(nSteps, 1.0e-6 * range, 0.1 * range, 1.0e-9, 1.0e-9);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (nSteps < 4) {
+ assertTrue(integ.getEvaluations() > 140);
+ } else {
+ assertTrue(integ.getEvaluations() < 90);
+ }
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
new file mode 100644
index 0000000..6c44c9d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import junit.framework.*;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemFactory;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.ClassicalRungeKuttaIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class ClassicalRungeKuttaIntegratorTest
+ extends TestCase {
+
+ public ClassicalRungeKuttaIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testMissedEndEvent() throws IntegratorException, DerivativeException {
+ final double t0 = 1878250320.0000029;
+ final double tEvent = 1878250379.9999986;
+ final double[] k = { 1.0e-4, 1.0e-5, 1.0e-6 };
+ FirstOrderDifferentialEquations ode = new FirstOrderDifferentialEquations() {
+
+ public int getDimension() {
+ return k.length;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ for (int i = 0; i < y.length; ++i) {
+ yDot[i] = k[i] * y[i];
+ }
+ }
+ };
+
+ ClassicalRungeKuttaIntegrator integrator = new ClassicalRungeKuttaIntegrator(60.0);
+
+ double[] y0 = new double[k.length];
+ for (int i = 0; i < y0.length; ++i) {
+ y0[i] = i + 1;
+ }
+ double[] y = new double[k.length];
+
+ double finalT = integrator.integrate(ode, t0, y0, tEvent, y);
+ Assert.assertEquals(tEvent, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * FastMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-9);
+ }
+
+ integrator.addEventHandler(new EventHandler() {
+
+ public void resetState(double t, double[] y) {
+ }
+
+ public double g(double t, double[] y) {
+ return t - tEvent;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ Assert.assertEquals(tEvent, t, 5.0e-6);
+ return CONTINUE;
+ }
+ }, Double.POSITIVE_INFINITY, 1.0e-20, 100);
+ finalT = integrator.integrate(ode, t0, y0, tEvent + 120, y);
+ Assert.assertEquals(tEvent + 120, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * FastMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-9);
+ }
+
+ }
+
+ public void testSanityChecks() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new ClassicalRungeKuttaIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new ClassicalRungeKuttaIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new ClassicalRungeKuttaIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()],
+ 0.0, new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract[] problems = TestProblemFactory.getProblems();
+ for (int k = 0; k < problems.length; ++k) {
+
+ double previousValueError = Double.NaN;
+ double previousTimeError = Double.NaN;
+ for (int i = 4; i < 10; ++i) {
+
+ TestProblemAbstract pb = problems[k].copy();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * FastMath.pow(2.0, -i);
+
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
+ }
+ assertEquals(functions.length, integ.getEventHandlers().size());
+ double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (functions.length == 0) {
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ double error = handler.getMaximalValueError();
+ if (i > 4) {
+ assertTrue(error < FastMath.abs(previousValueError));
+ }
+ previousValueError = error;
+
+ double timeError = handler.getMaximalTimeError();
+ if (i > 4) {
+ assertTrue(timeError <= FastMath.abs(previousTimeError));
+ }
+ previousTimeError = timeError;
+
+ integ.clearEventHandlers();
+ assertEquals(0, integ.getEventHandlers().size());
+ }
+
+ }
+
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-13);
+ assertTrue(handler.getMaximalValueError() < 4.0e-12);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("classical Runge-Kutta", integ.getName());
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
+
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() > 0.0004);
+ assertTrue(handler.getMaximalValueError() > 0.005);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double step = FastMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 5.0e-10);
+ assertTrue(handler.getMaximalValueError() < 7.0e-10);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("classical Runge-Kutta", integ.getName());
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ integ.addStepHandler(new KeplerHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ }
+
+ private static class KeplerHandler implements StepHandler {
+ public KeplerHandler(TestProblem3 pb) {
+ this.pb = pb;
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ maxError = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) throws DerivativeException {
+
+ double[] interpolatedY = interpolator.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getCurrentTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ if (isLast) {
+ // even with more than 1000 evaluations per period,
+ // RK4 is not able to integrate such an eccentric
+ // orbit with a good accuracy
+ assertTrue(maxError > 0.005);
+ }
+ }
+ private double maxError = 0;
+ private TestProblem3 pb;
+ }
+
+ public void testStepSize()
+ throws DerivativeException, IntegratorException {
+ final double step = 1.23456;
+ FirstOrderIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (! isLast) {
+ assertEquals(step,
+ interpolator.getCurrentTime() - interpolator.getPreviousTime(),
+ 1.0e-12);
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = 0L;
+ public void computeDerivatives(double t, double[] y, double[] dot) {
+ dot[0] = 1.0;
+ }
+ public int getDimension() {
+ return 1;
+ }
+ }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolatorTest.java
new file mode 100644
index 0000000..93f0805
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaStepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.junit.Test;
+
+public class ClassicalRungeKuttaStepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ ClassicalRungeKuttaIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+ ClassicalRungeKuttaIntegrator integ = new ClassicalRungeKuttaIntegrator(step);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 753000);
+ assertTrue(bos.size () < 754000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError > 0.005);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54IntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54IntegratorTest.java
new file mode 100644
index 0000000..c7be578
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54IntegratorTest.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem4;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator;
+import org.apache.commons.math.ode.nonstiff.EmbeddedRungeKuttaIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class DormandPrince54IntegratorTest
+ extends TestCase {
+
+ public DormandPrince54IntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ DormandPrince54Integrator integrator = new DormandPrince54Integrator(0.0, 1.0,
+ 1.0e-10, 1.0e-10);
+ integrator.integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testMinStep() {
+
+ try {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
+ double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
+
+ FirstOrderIntegrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+
+ }
+
+ public void testSmallLastStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract pb = new TestProblem5();
+ double minStep = 1.25;
+ double maxStep = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+ double scalAbsoluteTolerance = 6.0e-4;
+ double scalRelativeTolerance = 6.0e-4;
+
+ AdaptiveStepsizeIntegrator integ =
+ new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+
+ DP54SmallLastHandler handler = new DP54SmallLastHandler(minStep);
+ integ.addStepHandler(handler);
+ integ.setInitialStepSize(1.7);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertTrue(handler.wasLastSeen());
+ assertEquals("Dormand-Prince 5(4)", integ.getName());
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-7);
+ assertTrue(handler.getMaximalValueError() < 2.0e-7);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Dormand-Prince 5(4)", integ.getName());
+ }
+
+ private static class DP54SmallLastHandler implements StepHandler {
+
+ public DP54SmallLastHandler(double minStep) {
+ lastSeen = false;
+ this.minStep = minStep;
+ }
+
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+
+ public void reset() {
+ }
+
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (isLast) {
+ lastSeen = true;
+ double h = interpolator.getCurrentTime() - interpolator.getPreviousTime();
+ assertTrue(FastMath.abs(h) < minStep);
+ }
+ }
+
+ public boolean wasLastSeen() {
+ return lastSeen;
+ }
+
+ private boolean lastSeen;
+ private double minStep;
+
+ }
+
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -2; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = FastMath.pow(10.0, i);
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ EmbeddedRungeKuttaIntegrator integ =
+ new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.setSafety(0.8);
+ integ.setMaxGrowth(5.0);
+ integ.setMinReduction(0.3);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertEquals(0.8, integ.getSafety(), 1.0e-12);
+ assertEquals(5.0, integ.getMaxGrowth(), 1.0e-12);
+ assertEquals(0.3, integ.getMinReduction(), 1.0e-12);
+
+ // the 0.7 factor is only valid for this test
+ // and has been obtained from trial and error
+ // there is no general relation between local and global errors
+ assertTrue(handler.getMaximalValueError() < (0.7 * scalAbsoluteTolerance));
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ public void testEvents()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem4 pb = new TestProblem4();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ double convergence = 1.0e-8 * maxStep;
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, convergence, 1000);
+ }
+ assertEquals(functions.length, integ.getEventHandlers().size());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getMaximalValueError() < 5.0e-6);
+ assertEquals(0, handler.getMaximalTimeError(), convergence);
+ assertEquals(12.0, handler.getLastTime(), convergence);
+ integ.clearEventHandlers();
+ assertEquals(0, integ.getEventHandlers().size());
+
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new KeplerHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertEquals(integ.getEvaluations(), pb.getCalls());
+ assertTrue(pb.getCalls() < 2800);
+
+ }
+
+ public void testVariableSteps()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new VariableHandler());
+ double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ private static class KeplerHandler implements StepHandler {
+ public KeplerHandler(TestProblem3 pb) {
+ this.pb = pb;
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ nbSteps = 0;
+ maxError = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast)
+ throws DerivativeException {
+
+ ++nbSteps;
+ for (int a = 1; a < 10; ++a) {
+
+ double prev = interpolator.getPreviousTime();
+ double curr = interpolator.getCurrentTime();
+ double interp = ((10 - a) * prev + a * curr) / 10;
+ interpolator.setInterpolatedTime(interp);
+
+ double[] interpolatedY = interpolator.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getInterpolatedTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+ if (isLast) {
+ assertTrue(maxError < 7.0e-10);
+ assertTrue(nbSteps < 400);
+ }
+ }
+ private int nbSteps;
+ private double maxError;
+ private TestProblem3 pb;
+ }
+
+ private static class VariableHandler implements StepHandler {
+ public VariableHandler() {
+ firstTime = true;
+ minStep = 0;
+ maxStep = 0;
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ firstTime = true;
+ minStep = 0;
+ maxStep = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) {
+
+ double step = FastMath.abs(interpolator.getCurrentTime()
+ - interpolator.getPreviousTime());
+ if (firstTime) {
+ minStep = FastMath.abs(step);
+ maxStep = minStep;
+ firstTime = false;
+ } else {
+ if (step < minStep) {
+ minStep = step;
+ }
+ if (step > maxStep) {
+ maxStep = step;
+ }
+ }
+
+ if (isLast) {
+ assertTrue(minStep < (1.0 / 450.0));
+ assertTrue(maxStep > (1.0 / 4.2));
+ }
+ }
+ private boolean firstTime;
+ private double minStep;
+ private double maxStep;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolatorTest.java
new file mode 100644
index 0000000..5e3f57f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince54StepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class DormandPrince54StepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.1);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince54Integrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince54Integrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 126000);
+ assertTrue(bos.size () < 127000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 7.0e-10);
+
+ }
+
+ @Test
+ public void checkClone()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince54Integrator integ = new DormandPrince54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ StepInterpolator cloned = interpolator.copy();
+ double tA = cloned.getPreviousTime();
+ double tB = cloned.getCurrentTime();
+ double halfStep = FastMath.abs(tB - tA) / 2;
+ assertEquals(interpolator.getPreviousTime(), tA, 1.0e-12);
+ assertEquals(interpolator.getCurrentTime(), tB, 1.0e-12);
+ for (int i = 0; i < 10; ++i) {
+ double t = (i * tB + (9 - i) * tA) / 9;
+ interpolator.setInterpolatedTime(t);
+ assertTrue(FastMath.abs(cloned.getInterpolatedTime() - t) > (halfStep / 10));
+ cloned.setInterpolatedTime(t);
+ assertEquals(t, cloned.getInterpolatedTime(), 1.0e-12);
+ double[] referenceState = interpolator.getInterpolatedState();
+ double[] cloneState = cloned.getInterpolatedState();
+ for (int j = 0; j < referenceState.length; ++j) {
+ assertEquals(referenceState[j], cloneState[j], 1.0e-12);
+ }
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
new file mode 100644
index 0000000..c161235
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem4;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator;
+import org.apache.commons.math.ode.sampling.DummyStepHandler;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class DormandPrince853IntegratorTest
+ extends TestCase {
+
+ public DormandPrince853IntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testMissedEndEvent() throws IntegratorException, DerivativeException {
+ final double t0 = 1878250320.0000029;
+ final double tEvent = 1878250379.9999986;
+ final double[] k = { 1.0e-4, 1.0e-5, 1.0e-6 };
+ FirstOrderDifferentialEquations ode = new FirstOrderDifferentialEquations() {
+
+ public int getDimension() {
+ return k.length;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ for (int i = 0; i < y.length; ++i) {
+ yDot[i] = k[i] * y[i];
+ }
+ }
+ };
+
+ DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.0, 100.0,
+ 1.0e-10, 1.0e-10);
+
+ double[] y0 = new double[k.length];
+ for (int i = 0; i < y0.length; ++i) {
+ y0[i] = i + 1;
+ }
+ double[] y = new double[k.length];
+
+ integrator.setInitialStepSize(60.0);
+ double finalT = integrator.integrate(ode, t0, y0, tEvent, y);
+ Assert.assertEquals(tEvent, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * FastMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-9);
+ }
+
+ integrator.setInitialStepSize(60.0);
+ integrator.addEventHandler(new EventHandler() {
+
+ public void resetState(double t, double[] y) {
+ }
+
+ public double g(double t, double[] y) {
+ return t - tEvent;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ Assert.assertEquals(tEvent, t, 5.0e-6);
+ return CONTINUE;
+ }
+ }, Double.POSITIVE_INFINITY, 1.0e-20, 100);
+ finalT = integrator.integrate(ode, t0, y0, tEvent + 120, y);
+ Assert.assertEquals(tEvent + 120, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * FastMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-9);
+ }
+
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.0, 1.0,
+ 1.0e-10, 1.0e-10);
+ integrator.integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testNullIntervalCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.0, 1.0,
+ 1.0e-10, 1.0e-10);
+ integrator.integrate(pb,
+ 0.0, new double[pb.getDimension()],
+ 0.0, new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testMinStep() {
+
+ try {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
+ double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+
+ }
+
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -2; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = FastMath.pow(10.0, i);
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ // the 1.3 factor is only valid for this test
+ // and has been obtained from trial and error
+ // there is no general relation between local and global errors
+ assertTrue(handler.getMaximalValueError() < (1.3 * scalAbsoluteTolerance));
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 8.1e-8);
+ assertTrue(handler.getMaximalValueError() < 1.1e-7);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Dormand-Prince 8 (5, 3)", integ.getName());
+ }
+
+ public void testEvents()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem4 pb = new TestProblem4();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-9;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ double convergence = 1.0e-8 * maxStep;
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, convergence, 1000);
+ }
+ assertEquals(functions.length, integ.getEventHandlers().size());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getMaximalValueError() < 5.0e-8);
+ assertEquals(0, handler.getMaximalTimeError(), convergence);
+ assertEquals(12.0, handler.getLastTime(), convergence);
+ integ.clearEventHandlers();
+ assertEquals(0, integ.getEventHandlers().size());
+
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new KeplerHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertEquals(integ.getEvaluations(), pb.getCalls());
+ assertTrue(pb.getCalls() < 3300);
+
+ }
+
+ public void testVariableSteps()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new VariableHandler());
+ double stopTime = integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ assertEquals("Dormand-Prince 8 (5, 3)", integ.getName());
+ }
+
+ public void testNoDenseOutput()
+ throws DerivativeException, IntegratorException {
+ TestProblem1 pb1 = new TestProblem1();
+ TestProblem1 pb2 = pb1.copy();
+ double minStep = 0.1 * (pb1.getFinalTime() - pb1.getInitialTime());
+ double maxStep = pb1.getFinalTime() - pb1.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-4;
+ double scalRelativeTolerance = 1.0e-4;
+
+ FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(DummyStepHandler.getInstance());
+ integ.integrate(pb1,
+ pb1.getInitialTime(), pb1.getInitialState(),
+ pb1.getFinalTime(), new double[pb1.getDimension()]);
+ int callsWithoutDenseOutput = pb1.getCalls();
+ assertEquals(integ.getEvaluations(), callsWithoutDenseOutput);
+
+ integ.addStepHandler(new InterpolatingStepHandler());
+ integ.integrate(pb2,
+ pb2.getInitialTime(), pb2.getInitialState(),
+ pb2.getFinalTime(), new double[pb2.getDimension()]);
+ int callsWithDenseOutput = pb2.getCalls();
+ assertEquals(integ.getEvaluations(), callsWithDenseOutput);
+
+ assertTrue(callsWithDenseOutput > callsWithoutDenseOutput);
+
+ }
+
+ public void testUnstableDerivative()
+ throws DerivativeException, IntegratorException {
+ final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
+ FirstOrderIntegrator integ =
+ new DormandPrince853Integrator(0.1, 10, 1.0e-12, 0.0);
+ integ.addEventHandler(stepProblem, 1.0, 1.0e-12, 1000);
+ double[] y = { Double.NaN };
+ integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
+ assertEquals(8.0, y[0], 1.0e-12);
+ }
+
+ private static class KeplerHandler implements StepHandler {
+ public KeplerHandler(TestProblem3 pb) {
+ this.pb = pb;
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ nbSteps = 0;
+ maxError = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast)
+ throws DerivativeException {
+
+ ++nbSteps;
+ for (int a = 1; a < 10; ++a) {
+
+ double prev = interpolator.getPreviousTime();
+ double curr = interpolator.getCurrentTime();
+ double interp = ((10 - a) * prev + a * curr) / 10;
+ interpolator.setInterpolatedTime(interp);
+
+ double[] interpolatedY = interpolator.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getInterpolatedTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+ if (isLast) {
+ assertTrue(maxError < 2.4e-10);
+ assertTrue(nbSteps < 150);
+ }
+ }
+ private int nbSteps;
+ private double maxError;
+ private TestProblem3 pb;
+ }
+
+ private static class VariableHandler implements StepHandler {
+ public VariableHandler() {
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ firstTime = true;
+ minStep = 0;
+ maxStep = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) {
+
+ double step = FastMath.abs(interpolator.getCurrentTime()
+ - interpolator.getPreviousTime());
+ if (firstTime) {
+ minStep = FastMath.abs(step);
+ maxStep = minStep;
+ firstTime = false;
+ } else {
+ if (step < minStep) {
+ minStep = step;
+ }
+ if (step > maxStep) {
+ maxStep = step;
+ }
+ }
+
+ if (isLast) {
+ assertTrue(minStep < (1.0 / 100.0));
+ assertTrue(maxStep > (1.0 / 2.0));
+ }
+ }
+ private boolean firstTime = true;
+ private double minStep = 0;
+ private double maxStep = 0;
+ }
+
+ private static class InterpolatingStepHandler implements StepHandler {
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast)
+ throws DerivativeException {
+ double prev = interpolator.getPreviousTime();
+ double curr = interpolator.getCurrentTime();
+ interpolator.setInterpolatedTime(0.5*(prev + curr));
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolatorTest.java
new file mode 100644
index 0000000..fac6580
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853StepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class DormandPrince853StepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.1);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince853Integrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince853Integrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 88000);
+ assertTrue(bos.size () < 89000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 2.4e-10);
+
+ }
+
+ @Test
+ public void checklone()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ DormandPrince853Integrator integ = new DormandPrince853Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ StepInterpolator cloned = interpolator.copy();
+ double tA = cloned.getPreviousTime();
+ double tB = cloned.getCurrentTime();
+ double halfStep = FastMath.abs(tB - tA) / 2;
+ assertEquals(interpolator.getPreviousTime(), tA, 1.0e-12);
+ assertEquals(interpolator.getCurrentTime(), tB, 1.0e-12);
+ for (int i = 0; i < 10; ++i) {
+ double t = (i * tB + (9 - i) * tA) / 9;
+ interpolator.setInterpolatedTime(t);
+ assertTrue(FastMath.abs(cloned.getInterpolatedTime() - t) > (halfStep / 10));
+ cloned.setInterpolatedTime(t);
+ assertEquals(t, cloned.getInterpolatedTime(), 1.0e-12);
+ double[] referenceState = interpolator.getInterpolatedState();
+ double[] cloneState = cloned.getInterpolatedState();
+ for (int j = 0; j < referenceState.length; ++j) {
+ assertEquals(referenceState[j], cloneState[j], 1.0e-12);
+ }
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/EulerIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/EulerIntegratorTest.java
new file mode 100644
index 0000000..9b83dff
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/EulerIntegratorTest.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.nonstiff;
+
+import junit.framework.*;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemFactory;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.EulerIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class EulerIntegratorTest
+ extends TestCase {
+
+ public EulerIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new EulerIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract[] problems = TestProblemFactory.getProblems();
+ for (int k = 0; k < problems.length; ++k) {
+
+ double previousValueError = Double.NaN;
+ double previousTimeError = Double.NaN;
+ for (int i = 4; i < 8; ++i) {
+
+ TestProblemAbstract pb = problems[k].copy();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * FastMath.pow(2.0, -i);
+
+ FirstOrderIntegrator integ = new EulerIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
+ }
+ double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (functions.length == 0) {
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ double valueError = handler.getMaximalValueError();
+ if (i > 4) {
+ assertTrue(valueError < FastMath.abs(previousValueError));
+ }
+ previousValueError = valueError;
+
+ double timeError = handler.getMaximalTimeError();
+ if (i > 4) {
+ assertTrue(timeError <= FastMath.abs(previousTimeError));
+ }
+ previousTimeError = timeError;
+
+ }
+
+ }
+
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new EulerIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-4);
+ assertTrue(handler.getMaximalValueError() < 1.0e-3);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Euler", integ.getName());
+
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
+
+ FirstOrderIntegrator integ = new EulerIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() > 0.01);
+ assertTrue(handler.getMaximalValueError() > 0.2);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double step = FastMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new EulerIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 0.45);
+ assertTrue(handler.getMaximalValueError() < 0.45);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Euler", integ.getName());
+ }
+
+ public void testStepSize()
+ throws DerivativeException, IntegratorException {
+ final double step = 1.23456;
+ FirstOrderIntegrator integ = new EulerIntegrator(step);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (! isLast) {
+ assertEquals(step,
+ interpolator.getCurrentTime() - interpolator.getPreviousTime(),
+ 1.0e-12);
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = 0L;
+ public void computeDerivatives(double t, double[] y, double[] dot) {
+ dot[0] = 1.0;
+ }
+ public int getDimension() {
+ return 1;
+ }
+ }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolatorTest.java
new file mode 100644
index 0000000..7f3324d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/EulerStepInterpolatorTest.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class EulerStepInterpolatorTest {
+
+ @Test
+ public void noReset() throws DerivativeException {
+
+ double[] y = { 0.0, 1.0, -2.0 };
+ double[][] yDot = { { 1.0, 2.0, -2.0 } };
+ EulerStepInterpolator interpolator = new EulerStepInterpolator();
+ interpolator.reinitialize(new DummyIntegrator(interpolator), y, yDot, true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ double[] result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ }
+
+ @Test
+ public void interpolationAtBounds()
+ throws DerivativeException {
+
+ double t0 = 0;
+ double[] y0 = {0.0, 1.0, -2.0};
+
+ double[] y = y0.clone();
+ double[][] yDot = { new double[y0.length] };
+ EulerStepInterpolator interpolator = new EulerStepInterpolator();
+ interpolator.reinitialize(new DummyIntegrator(interpolator), y, yDot, true);
+ interpolator.storeTime(t0);
+
+ double dt = 1.0;
+ y[0] = 1.0;
+ y[1] = 3.0;
+ y[2] = -4.0;
+ yDot[0][0] = (y[0] - y0[0]) / dt;
+ yDot[0][1] = (y[1] - y0[1]) / dt;
+ yDot[0][2] = (y[2] - y0[2]) / dt;
+ interpolator.shift();
+ interpolator.storeTime(t0 + dt);
+
+ interpolator.setInterpolatedTime(interpolator.getPreviousTime());
+ double[] result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y0[i]) < 1.0e-10);
+ }
+
+ interpolator.setInterpolatedTime(interpolator.getCurrentTime());
+ result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ }
+
+ @Test
+ public void interpolationInside()
+ throws DerivativeException {
+
+ double[] y = { 1.0, 3.0, -4.0 };
+ double[][] yDot = { { 1.0, 2.0, -2.0 } };
+ EulerStepInterpolator interpolator = new EulerStepInterpolator();
+ interpolator.reinitialize(new DummyIntegrator(interpolator), y, yDot, true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ interpolator.setInterpolatedTime(0.1);
+ double[] result = interpolator.getInterpolatedState();
+ assertTrue(FastMath.abs(result[0] - 0.1) < 1.0e-10);
+ assertTrue(FastMath.abs(result[1] - 1.2) < 1.0e-10);
+ assertTrue(FastMath.abs(result[2] + 2.2) < 1.0e-10);
+
+ interpolator.setInterpolatedTime(0.5);
+ result = interpolator.getInterpolatedState();
+ assertTrue(FastMath.abs(result[0] - 0.5) < 1.0e-10);
+ assertTrue(FastMath.abs(result[1] - 2.0) < 1.0e-10);
+ assertTrue(FastMath.abs(result[2] + 3.0) < 1.0e-10);
+
+ }
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ EulerIntegrator integ = new EulerIntegrator(step);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ EulerIntegrator integ = new EulerIntegrator(step);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+ assertTrue(maxError < 0.001);
+
+ }
+
+ private static class DummyIntegrator extends RungeKuttaIntegrator {
+
+
+ protected DummyIntegrator(RungeKuttaStepInterpolator prototype) {
+ super("dummy", new double[0], new double[0][0], new double[0], prototype, Double.NaN);
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/GillIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/GillIntegratorTest.java
new file mode 100644
index 0000000..0814a3f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/GillIntegratorTest.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import junit.framework.*;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemFactory;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.GillIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class GillIntegratorTest
+ extends TestCase {
+
+ public GillIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new GillIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract[] problems = TestProblemFactory.getProblems();
+ for (int k = 0; k < problems.length; ++k) {
+
+ double previousValueError = Double.NaN;
+ double previousTimeError = Double.NaN;
+ for (int i = 5; i < 10; ++i) {
+
+ TestProblemAbstract pb = problems[k].copy();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * FastMath.pow(2.0, -i);
+
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
+ }
+ double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (functions.length == 0) {
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ double valueError = handler.getMaximalValueError();
+ if (i > 5) {
+ assertTrue(valueError < FastMath.abs(previousValueError));
+ }
+ previousValueError = valueError;
+
+ double timeError = handler.getMaximalTimeError();
+ if (i > 5) {
+ assertTrue(timeError <= FastMath.abs(previousTimeError));
+ }
+ previousTimeError = timeError;
+
+ }
+
+ }
+
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-13);
+ assertTrue(handler.getMaximalValueError() < 4.0e-12);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Gill", integ.getName());
+
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
+
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() > 0.0004);
+ assertTrue(handler.getMaximalValueError() > 0.005);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double step = FastMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 5.0e-10);
+ assertTrue(handler.getMaximalValueError() < 7.0e-10);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Gill", integ.getName());
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ integ.addStepHandler(new KeplerStepHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ }
+
+ public void testUnstableDerivative()
+ throws DerivativeException, IntegratorException {
+ final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
+ FirstOrderIntegrator integ = new GillIntegrator(0.3);
+ integ.addEventHandler(stepProblem, 1.0, 1.0e-12, 1000);
+ double[] y = { Double.NaN };
+ integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
+ assertEquals(8.0, y[0], 1.0e-12);
+ }
+
+ private static class KeplerStepHandler implements StepHandler {
+ public KeplerStepHandler(TestProblem3 pb) {
+ this.pb = pb;
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ maxError = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) throws DerivativeException {
+
+ double[] interpolatedY = interpolator.getInterpolatedState();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getCurrentTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ if (isLast) {
+ // even with more than 1000 evaluations per period,
+ // RK4 is not able to integrate such an eccentric
+ // orbit with a good accuracy
+ assertTrue(maxError > 0.001);
+ }
+ }
+ private double maxError;
+ private TestProblem3 pb;
+ }
+
+ public void testStepSize()
+ throws DerivativeException, IntegratorException {
+ final double step = 1.23456;
+ FirstOrderIntegrator integ = new GillIntegrator(step);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (! isLast) {
+ assertEquals(step,
+ interpolator.getCurrentTime() - interpolator.getPreviousTime(),
+ 1.0e-12);
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = 0L;
+ public void computeDerivatives(double t, double[] y, double[] dot) {
+ dot[0] = 1.0;
+ }
+ public int getDimension() {
+ return 1;
+ }
+ }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolatorTest.java
new file mode 100644
index 0000000..7267850
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/GillStepInterpolatorTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Random;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.nonstiff.GillIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.junit.Test;
+
+public class GillStepInterpolatorTest {
+
+ @Test
+ public void testDerivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ GillIntegrator integ = new GillIntegrator(step);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+ GillIntegrator integ = new GillIntegrator(step);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 753000);
+ assertTrue(bos.size () < 754000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 0.003);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegratorTest.java
new file mode 100644
index 0000000..1f949a4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerIntegratorTest.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem4;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.GraggBulirschStoerIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class GraggBulirschStoerIntegratorTest
+ extends TestCase {
+
+ public GraggBulirschStoerIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ AdaptiveStepsizeIntegrator integrator =
+ new GraggBulirschStoerIntegrator(0.0, 1.0, 1.0e-10, 1.0e-10);
+ integrator.integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testNullIntervalCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ GraggBulirschStoerIntegrator integrator =
+ new GraggBulirschStoerIntegrator(0.0, 1.0, 1.0e-10, 1.0e-10);
+ integrator.integrate(pb,
+ 0.0, new double[pb.getDimension()],
+ 0.0, new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testMinStep() {
+
+ try {
+ TestProblem5 pb = new TestProblem5();
+ double minStep = 0.1 * FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = FastMath.abs(pb.getFinalTime() - pb.getInitialTime());
+ double[] vecAbsoluteTolerance = { 1.0e-20, 1.0e-21 };
+ double[] vecRelativeTolerance = { 1.0e-20, 1.0e-21 };
+
+ FirstOrderIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new GraggBulirschStoerIntegrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 7.5e-9);
+ assertTrue(handler.getMaximalValueError() < 8.1e-9);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Gragg-Bulirsch-Stoer", integ.getName());
+ }
+
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -4; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double absTolerance = FastMath.pow(10.0, i);
+ double relTolerance = absTolerance;
+
+ FirstOrderIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ absTolerance, relTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ // the coefficients are only valid for this test
+ // and have been obtained from trial and error
+ // there is no general relation between local and global errors
+ double ratio = handler.getMaximalValueError() / absTolerance;
+ assertTrue(ratio < 2.4);
+ assertTrue(ratio > 0.02);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ public void testIntegratorControls()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem3 pb = new TestProblem3(0.999);
+ GraggBulirschStoerIntegrator integ =
+ new GraggBulirschStoerIntegrator(0, pb.getFinalTime() - pb.getInitialTime(),
+ 1.0e-8, 1.0e-10);
+
+ double errorWithDefaultSettings = getMaxError(integ, pb);
+
+ // stability control
+ integ.setStabilityCheck(true, 2, 1, 0.99);
+ assertTrue(errorWithDefaultSettings < getMaxError(integ, pb));
+ integ.setStabilityCheck(true, -1, -1, -1);
+
+ integ.setStepsizeControl(0.5, 0.99, 0.1, 2.5);
+ assertTrue(errorWithDefaultSettings < getMaxError(integ, pb));
+ integ.setStepsizeControl(-1, -1, -1, -1);
+
+ integ.setOrderControl(10, 0.7, 0.95);
+ assertTrue(errorWithDefaultSettings < getMaxError(integ, pb));
+ integ.setOrderControl(-1, -1, -1);
+
+ integ.setInterpolationControl(true, 3);
+ assertTrue(errorWithDefaultSettings < getMaxError(integ, pb));
+ integ.setInterpolationControl(true, -1);
+
+ }
+
+ private double getMaxError(FirstOrderIntegrator integrator, TestProblemAbstract pb)
+ throws DerivativeException, IntegratorException {
+ TestProblemHandler handler = new TestProblemHandler(pb, integrator);
+ integrator.addStepHandler(handler);
+ integrator.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ return handler.getMaximalValueError();
+ }
+
+ public void testEvents()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem4 pb = new TestProblem4();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-10;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new GraggBulirschStoerIntegrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ double convergence = 1.0e-8 * maxStep;
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l], Double.POSITIVE_INFINITY, convergence, 1000);
+ }
+ assertEquals(functions.length, integ.getEventHandlers().size());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getMaximalValueError() < 5.0e-8);
+ assertEquals(0, handler.getMaximalTimeError(), convergence);
+ assertEquals(12.0, handler.getLastTime(), convergence);
+ integ.clearEventHandlers();
+ assertEquals(0, integ.getEventHandlers().size());
+
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double absTolerance = 1.0e-6;
+ double relTolerance = 1.0e-6;
+
+ FirstOrderIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ absTolerance, relTolerance);
+ integ.addStepHandler(new KeplerStepHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertEquals(integ.getEvaluations(), pb.getCalls());
+ assertTrue(pb.getCalls() < 2150);
+
+ }
+
+ public void testVariableSteps()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double absTolerance = 1.0e-8;
+ double relTolerance = 1.0e-8;
+ FirstOrderIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ absTolerance, relTolerance);
+ integ.addStepHandler(new VariableStepHandler());
+ double stopTime = integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ assertEquals("Gragg-Bulirsch-Stoer", integ.getName());
+ }
+
+ public void testUnstableDerivative()
+ throws DerivativeException, IntegratorException {
+ final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
+ FirstOrderIntegrator integ =
+ new GraggBulirschStoerIntegrator(0.1, 10, 1.0e-12, 0.0);
+ integ.addEventHandler(stepProblem, 1.0, 1.0e-12, 1000);
+ double[] y = { Double.NaN };
+ integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
+ assertEquals(8.0, y[0], 1.0e-12);
+ }
+
+ private static class KeplerStepHandler implements StepHandler {
+ public KeplerStepHandler(TestProblem3 pb) {
+ this.pb = pb;
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ nbSteps = 0;
+ maxError = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast)
+ throws DerivativeException {
+
+ ++nbSteps;
+ for (int a = 1; a < 100; ++a) {
+
+ double prev = interpolator.getPreviousTime();
+ double curr = interpolator.getCurrentTime();
+ double interp = ((100 - a) * prev + a * curr) / 100;
+ interpolator.setInterpolatedTime(interp);
+
+ double[] interpolatedY = interpolator.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getInterpolatedTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+ if (isLast) {
+ assertTrue(maxError < 2.7e-6);
+ assertTrue(nbSteps < 80);
+ }
+ }
+ private int nbSteps;
+ private double maxError;
+ private TestProblem3 pb;
+ }
+
+ public static class VariableStepHandler implements StepHandler {
+ public VariableStepHandler() {
+ reset();
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ firstTime = true;
+ minStep = 0;
+ maxStep = 0;
+ }
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) {
+
+ double step = FastMath.abs(interpolator.getCurrentTime()
+ - interpolator.getPreviousTime());
+ if (firstTime) {
+ minStep = FastMath.abs(step);
+ maxStep = minStep;
+ firstTime = false;
+ } else {
+ if (step < minStep) {
+ minStep = step;
+ }
+ if (step > maxStep) {
+ maxStep = step;
+ }
+ }
+
+ if (isLast) {
+ assertTrue(minStep < 8.2e-3);
+ assertTrue(maxStep > 1.7);
+ }
+ }
+ private boolean firstTime;
+ private double minStep;
+ private double maxStep;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolatorTest.java
new file mode 100644
index 0000000..82d9372
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/GraggBulirschStoerStepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class GraggBulirschStoerStepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double absTolerance = 1.0e-8;
+ double relTolerance = 1.0e-8;
+
+ GraggBulirschStoerIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ absTolerance, relTolerance);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-8);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double absTolerance = 1.0e-8;
+ double relTolerance = 1.0e-8;
+
+ GraggBulirschStoerIntegrator integ =
+ new GraggBulirschStoerIntegrator(minStep, maxStep,
+ absTolerance, relTolerance);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 34000);
+ assertTrue(bos.size () < 35000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 5.0e-10);
+
+ }
+
+ @Test
+ public void checklone()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ GraggBulirschStoerIntegrator integ = new GraggBulirschStoerIntegrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ StepInterpolator cloned = interpolator.copy();
+ double tA = cloned.getPreviousTime();
+ double tB = cloned.getCurrentTime();
+ double halfStep = FastMath.abs(tB - tA) / 2;
+ assertEquals(interpolator.getPreviousTime(), tA, 1.0e-12);
+ assertEquals(interpolator.getCurrentTime(), tB, 1.0e-12);
+ for (int i = 0; i < 10; ++i) {
+ double t = (i * tB + (9 - i) * tA) / 9;
+ interpolator.setInterpolatedTime(t);
+ assertTrue(FastMath.abs(cloned.getInterpolatedTime() - t) > (halfStep / 10));
+ cloned.setInterpolatedTime(t);
+ assertEquals(t, cloned.getInterpolatedTime(), 1.0e-12);
+ double[] referenceState = interpolator.getInterpolatedState();
+ double[] cloneState = cloned.getInterpolatedState();
+ for (int j = 0; j < referenceState.length; ++j) {
+ assertEquals(referenceState[j], cloneState[j], 1.0e-12);
+ }
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54IntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54IntegratorTest.java
new file mode 100644
index 0000000..156e2bd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54IntegratorTest.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.ode.nonstiff;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem4;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventException;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.util.FastMath;
+
+public class HighamHall54IntegratorTest
+ extends TestCase {
+
+ public HighamHall54IntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testWrongDerivative() throws Exception {
+ HighamHall54Integrator integrator =
+ new HighamHall54Integrator(0.0, 1.0, 1.0e-10, 1.0e-10);
+ FirstOrderDifferentialEquations equations =
+ new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = -1157081786301178032L;
+ public void computeDerivatives(double t, double[] y, double[] dot)
+ throws DerivativeException {
+ if (t < -0.5) {
+ throw new DerivativeException(LocalizedFormats.SIMPLE_MESSAGE, "oops");
+ } else {
+ throw new DerivativeException(new RuntimeException("oops"));
+ }
+ }
+ public int getDimension() {
+ return 1;
+ }
+ };
+
+ try {
+ integrator.integrate(equations, -1.0, new double[1], 0.0, new double[1]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ // expected behavior
+ }
+
+ try {
+ integrator.integrate(equations, 0.0, new double[1], 1.0, new double[1]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ // expected behavior
+ }
+
+ }
+
+ public void testMinStep() {
+
+ try {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
+ double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
+
+ FirstOrderIntegrator integ = new HighamHall54Integrator(minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+
+ }
+
+ public void testIncreasingTolerance()
+ throws DerivativeException, IntegratorException {
+
+ int previousCalls = Integer.MAX_VALUE;
+ for (int i = -12; i < -2; ++i) {
+ TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = FastMath.pow(10.0, i);
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ // the 1.3 factor is only valid for this test
+ // and has been obtained from trial and error
+ // there is no general relation between local and global errors
+ assertTrue(handler.getMaximalValueError() < (1.3 * scalAbsoluteTolerance));
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ int calls = pb.getCalls();
+ assertEquals(integ.getEvaluations(), calls);
+ assertTrue(calls <= previousCalls);
+ previousCalls = calls;
+
+ }
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 5.0e-7);
+ assertTrue(handler.getMaximalValueError() < 5.0e-7);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("Higham-Hall 5(4)", integ.getName());
+ }
+
+ public void testEvents()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem4 pb = new TestProblem4();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ double convergence = 1.0e-8 * maxStep;
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, convergence, 1000);
+ }
+ assertEquals(functions.length, integ.getEventHandlers().size());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getMaximalValueError() < 1.0e-7);
+ assertEquals(0, handler.getMaximalTimeError(), convergence);
+ assertEquals(12.0, handler.getLastTime(), convergence);
+ integ.clearEventHandlers();
+ assertEquals(0, integ.getEventHandlers().size());
+
+ }
+
+ public void testEventsErrors() throws Exception {
+
+ final TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+
+ integ.addEventHandler(new EventHandler() {
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ return EventHandler.CONTINUE;
+ }
+ public double g(double t, double[] y) throws EventException {
+ double middle = (pb.getInitialTime() + pb.getFinalTime()) / 2;
+ double offset = t - middle;
+ if (offset > 0) {
+ throw new EventException(LocalizedFormats.EVALUATION_FAILED, t);
+ }
+ return offset;
+ }
+ public void resetState(double t, double[] y) {
+ }
+ private static final long serialVersionUID = 935652725339916361L;
+ }, Double.POSITIVE_INFINITY, 1.0e-8 * maxStep, 1000);
+
+ try {
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ }
+
+ public void testEventsNoConvergence() throws Exception {
+
+ final TestProblem1 pb = new TestProblem1();
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
+
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+
+ integ.addEventHandler(new EventHandler() {
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ return EventHandler.CONTINUE;
+ }
+ public double g(double t, double[] y) {
+ double middle = (pb.getInitialTime() + pb.getFinalTime()) / 2;
+ double offset = t - middle;
+ return (offset > 0) ? (offset + 0.5) : (offset - 0.5);
+ }
+ public void resetState(double t, double[] y) {
+ }
+ private static final long serialVersionUID = 935652725339916361L;
+ }, Double.POSITIVE_INFINITY, 1.0e-8 * maxStep, 3);
+
+ try {
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ assertTrue(ie.getCause() != null);
+ assertTrue(ie.getCause() instanceof ConvergenceException);
+ }
+
+}
+
+ public void testSanityChecks() throws Exception {
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+
+ try {
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep, new double[4], new double[4]);
+ integ.integrate(pb, pb.getInitialTime(), new double[6],
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ try {
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep, new double[4], new double[4]);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[6]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ try {
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep, new double[2], new double[4]);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ try {
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep, new double[4], new double[2]);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ try {
+ FirstOrderIntegrator integ =
+ new HighamHall54Integrator(minStep, maxStep, new double[4], new double[4]);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getInitialTime(), new double[pb.getDimension()]);
+ fail("an exception should have been thrown");
+ } catch (IntegratorException ie) {
+ // expected behavior
+ }
+
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double[] vecAbsoluteTolerance = { 1.0e-8, 1.0e-8, 1.0e-10, 1.0e-10 };
+ double[] vecRelativeTolerance = { 1.0e-10, 1.0e-10, 1.0e-8, 1.0e-8 };
+
+ FirstOrderIntegrator integ = new HighamHall54Integrator(minStep, maxStep,
+ vecAbsoluteTolerance,
+ vecRelativeTolerance);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertEquals(0.0, handler.getMaximalValueError(), 1.5e-4);
+ assertEquals("Higham-Hall 5(4)", integ.getName());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolatorTest.java
new file mode 100644
index 0000000..4b7136c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/HighamHall54StepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class HighamHall54StepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.1);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ HighamHall54Integrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.1e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ HighamHall54Integrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 167000);
+ assertTrue(bos.size () < 168000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 1.6e-10);
+
+ }
+
+ @Test
+ public void checkClone()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ double scalAbsoluteTolerance = 1.0e-8;
+ double scalRelativeTolerance = scalAbsoluteTolerance;
+ HighamHall54Integrator integ = new HighamHall54Integrator(minStep, maxStep,
+ scalAbsoluteTolerance,
+ scalRelativeTolerance);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+ StepInterpolator cloned = interpolator.copy();
+ double tA = cloned.getPreviousTime();
+ double tB = cloned.getCurrentTime();
+ double halfStep = FastMath.abs(tB - tA) / 2;
+ assertEquals(interpolator.getPreviousTime(), tA, 1.0e-12);
+ assertEquals(interpolator.getCurrentTime(), tB, 1.0e-12);
+ for (int i = 0; i < 10; ++i) {
+ double t = (i * tB + (9 - i) * tA) / 9;
+ interpolator.setInterpolatedTime(t);
+ assertTrue(FastMath.abs(cloned.getInterpolatedTime() - t) > (halfStep / 10));
+ cloned.setInterpolatedTime(t);
+ assertEquals(t, cloned.getInterpolatedTime(), 1.0e-12);
+ double[] referenceState = interpolator.getInterpolatedState();
+ double[] cloneState = cloned.getInterpolatedState();
+ for (int j = 0; j < referenceState.length; ++j) {
+ assertEquals(referenceState[j], cloneState[j], 1.0e-12);
+ }
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointIntegratorTest.java
new file mode 100644
index 0000000..c85143e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointIntegratorTest.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.nonstiff;
+
+import junit.framework.*;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemFactory;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.MidpointIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class MidpointIntegratorTest
+ extends TestCase {
+
+ public MidpointIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new MidpointIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract[] problems = TestProblemFactory.getProblems();
+ for (int k = 0; k < problems.length; ++k) {
+
+ double previousValueError = Double.NaN;
+ double previousTimeError = Double.NaN;
+ for (int i = 4; i < 10; ++i) {
+
+ TestProblemAbstract pb = problems[k].copy();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * FastMath.pow(2.0, -i);
+ FirstOrderIntegrator integ = new MidpointIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
+ }
+ double stopTime = integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (functions.length == 0) {
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ double valueError = handler.getMaximalValueError();
+ if (i > 4) {
+ assertTrue(valueError < FastMath.abs(previousValueError));
+ }
+ previousValueError = valueError;
+
+ double timeError = handler.getMaximalTimeError();
+ if (i > 4) {
+ assertTrue(timeError <= FastMath.abs(previousTimeError));
+ }
+ previousTimeError = timeError;
+
+ }
+
+ }
+
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new MidpointIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-7);
+ assertTrue(handler.getMaximalValueError() < 1.0e-6);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("midpoint", integ.getName());
+
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
+
+ FirstOrderIntegrator integ = new MidpointIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() > 0.01);
+ assertTrue(handler.getMaximalValueError() > 0.05);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double step = FastMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new MidpointIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 6.0e-4);
+ assertTrue(handler.getMaximalValueError() < 6.0e-4);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("midpoint", integ.getName());
+ }
+
+ public void testStepSize()
+ throws DerivativeException, IntegratorException {
+ final double step = 1.23456;
+ FirstOrderIntegrator integ = new MidpointIntegrator(step);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (! isLast) {
+ assertEquals(step,
+ interpolator.getCurrentTime() - interpolator.getPreviousTime(),
+ 1.0e-12);
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(new FirstOrderDifferentialEquations() {
+ private static final long serialVersionUID = 0L;
+ public void computeDerivatives(double t, double[] y, double[] dot) {
+ dot[0] = 1.0;
+ }
+ public int getDimension() {
+ return 1;
+ }
+ }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolatorTest.java
new file mode 100644
index 0000000..7ae2d3a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/MidpointStepInterpolatorTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.nonstiff;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.junit.Test;
+
+public class MidpointStepInterpolatorTest {
+
+ @Test
+ public void testDerivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ MidpointIntegrator integ = new MidpointIntegrator(step);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ MidpointIntegrator integ = new MidpointIntegrator(step);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 114000);
+ assertTrue(bos.size () < 115000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 1.0e-6);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/StepProblem.java b/src/test/java/org/apache/commons/math/ode/nonstiff/StepProblem.java
new file mode 100644
index 0000000..dc5420b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/StepProblem.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.nonstiff;
+
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.events.EventHandler;
+
+
+public class StepProblem
+ implements FirstOrderDifferentialEquations, EventHandler {
+
+ public StepProblem(double rateBefore, double rateAfter,
+ double switchTime) {
+ this.rateAfter = rateAfter;
+ this.switchTime = switchTime;
+ setRate(rateBefore);
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ yDot[0] = rate;
+ }
+
+ public int getDimension() {
+ return 1;
+ }
+
+ public void setRate(double rate) {
+ this.rate = rate;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ setRate(rateAfter);
+ return RESET_DERIVATIVES;
+ }
+
+ public double g(double t, double[] y) {
+ return t - switchTime;
+ }
+
+ public void resetState(double t, double[] y) {
+ }
+
+ private double rate;
+ private double rateAfter;
+ private double switchTime;
+
+ private static final long serialVersionUID = 7590601995477504318L;
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegratorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegratorTest.java
new file mode 100644
index 0000000..9f24dc9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesIntegratorTest.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.ode.nonstiff;
+
+import junit.framework.*;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.TestProblem5;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.ode.TestProblemFactory;
+import org.apache.commons.math.ode.TestProblemHandler;
+import org.apache.commons.math.ode.events.EventHandler;
+import org.apache.commons.math.ode.nonstiff.ThreeEighthesIntegrator;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
+import org.apache.commons.math.util.FastMath;
+
+public class ThreeEighthesIntegratorTest
+ extends TestCase {
+
+ public ThreeEighthesIntegratorTest(String name) {
+ super(name);
+ }
+
+ public void testDimensionCheck() {
+ try {
+ TestProblem1 pb = new TestProblem1();
+ new ThreeEighthesIntegrator(0.01).integrate(pb,
+ 0.0, new double[pb.getDimension()+10],
+ 1.0, new double[pb.getDimension()+10]);
+ fail("an exception should have been thrown");
+ } catch(DerivativeException de) {
+ fail("wrong exception caught");
+ } catch(IntegratorException ie) {
+ }
+ }
+
+ public void testDecreasingSteps()
+ throws DerivativeException, IntegratorException {
+
+ TestProblemAbstract[] problems = TestProblemFactory.getProblems();
+ for (int k = 0; k < problems.length; ++k) {
+
+ double previousValueError = Double.NaN;
+ double previousTimeError = Double.NaN;
+ for (int i = 4; i < 10; ++i) {
+
+ TestProblemAbstract pb = problems[k].copy();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * FastMath.pow(2.0, -i);
+
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ EventHandler[] functions = pb.getEventsHandlers();
+ for (int l = 0; l < functions.length; ++l) {
+ integ.addEventHandler(functions[l],
+ Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
+ }
+ double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ if (functions.length == 0) {
+ assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
+ }
+
+ double error = handler.getMaximalValueError();
+ if (i > 4) {
+ assertTrue(error < FastMath.abs(previousValueError));
+ }
+ previousValueError = error;
+
+ double timeError = handler.getMaximalTimeError();
+ if (i > 4) {
+ assertTrue(timeError <= FastMath.abs(previousTimeError));
+ }
+ previousTimeError = timeError;
+
+ }
+
+ }
+
+ }
+
+ public void testSmallStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 2.0e-13);
+ assertTrue(handler.getMaximalValueError() < 4.0e-12);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("3/8", integ.getName());
+
+ }
+
+ public void testBigStep()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem1 pb = new TestProblem1();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
+
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() > 0.0004);
+ assertTrue(handler.getMaximalValueError() > 0.005);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+
+ }
+
+ public void testBackward()
+ throws DerivativeException, IntegratorException {
+
+ TestProblem5 pb = new TestProblem5();
+ double step = FastMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ TestProblemHandler handler = new TestProblemHandler(pb, integ);
+ integ.addStepHandler(handler);
+ integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ assertTrue(handler.getLastError() < 5.0e-10);
+ assertTrue(handler.getMaximalValueError() < 7.0e-10);
+ assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
+ assertEquals("3/8", integ.getName());
+ }
+
+ public void testKepler()
+ throws DerivativeException, IntegratorException {
+
+ final TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ integ.addStepHandler(new KeplerHandler(pb));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ }
+
+ private static class KeplerHandler implements StepHandler {
+
+ public KeplerHandler(TestProblem3 pb) {
+ this.pb = pb;
+ maxError = 0;
+ }
+
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+
+ public void reset() {
+ maxError = 0;
+ }
+
+ public void handleStep(StepInterpolator interpolator,
+ boolean isLast) throws DerivativeException {
+
+ double[] interpolatedY = interpolator.getInterpolatedState();
+ double[] theoreticalY = pb.computeTheoreticalState(interpolator.getCurrentTime());
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ if (isLast) {
+ // even with more than 1000 evaluations per period,
+ // RK4 is not able to integrate such an eccentric
+ // orbit with a good accuracy
+ assertTrue(maxError > 0.005);
+ }
+ }
+
+ private TestProblem3 pb;
+ private double maxError = 0;
+
+ }
+
+ public void testStepSize()
+ throws DerivativeException, IntegratorException {
+ final double step = 1.23456;
+ FirstOrderIntegrator integ = new ThreeEighthesIntegrator(step);
+ integ.addStepHandler(new StepHandler() {
+ public void handleStep(StepInterpolator interpolator, boolean isLast) {
+ if (! isLast) {
+ assertEquals(step,
+ interpolator.getCurrentTime() - interpolator.getPreviousTime(),
+ 1.0e-12);
+ }
+ }
+ public boolean requiresDenseOutput() {
+ return false;
+ }
+ public void reset() {
+ }
+ });
+ integ.integrate(new FirstOrderDifferentialEquations() {
+ public void computeDerivatives(double t, double[] y, double[] dot) {
+ dot[0] = 1.0;
+ }
+ public int getDimension() {
+ return 1;
+ }
+ }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolatorTest.java
new file mode 100644
index 0000000..99fc6d4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/nonstiff/ThreeEighthesStepInterpolatorTest.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.ode.nonstiff;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolatorTestUtils;
+import org.junit.Test;
+
+public class ThreeEighthesStepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
+ ThreeEighthesIntegrator integ = new ThreeEighthesIntegrator(step);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 1.0e-10);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem3 pb = new TestProblem3(0.9);
+ double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
+ ThreeEighthesIntegrator integ = new ThreeEighthesIntegrator(step);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 753000);
+ assertTrue(bos.size () < 754000);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError > 0.005);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/sampling/DummyStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/sampling/DummyStepInterpolatorTest.java
new file mode 100644
index 0000000..ddd40c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/sampling/DummyStepInterpolatorTest.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.ode.sampling;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.exception.util.Localizable;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class DummyStepInterpolatorTest {
+
+ @Test
+ public void testNoReset() throws DerivativeException {
+
+ double[] y = { 0.0, 1.0, -2.0 };
+ DummyStepInterpolator interpolator = new DummyStepInterpolator(y, new double[y.length], true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ double[] result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ }
+
+ @Test
+ public void testFixedState()
+ throws DerivativeException {
+
+ double[] y = { 1.0, 3.0, -4.0 };
+ DummyStepInterpolator interpolator = new DummyStepInterpolator(y, new double[y.length], true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ interpolator.setInterpolatedTime(0.1);
+ double[] result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ interpolator.setInterpolatedTime(0.5);
+ result = interpolator.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ }
+
+ @Test
+ public void testSerialization()
+ throws DerivativeException, IOException, ClassNotFoundException {
+
+ double[] y = { 0.0, 1.0, -2.0 };
+ DummyStepInterpolator interpolator = new DummyStepInterpolator(y, new double[y.length], true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(interpolator);
+
+ assertTrue(bos.size () > 200);
+ assertTrue(bos.size () < 300);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ DummyStepInterpolator dsi = (DummyStepInterpolator) ois.readObject();
+
+ dsi.setInterpolatedTime(0.5);
+ double[] result = dsi.getInterpolatedState();
+ for (int i = 0; i < result.length; ++i) {
+ assertTrue(FastMath.abs(result[i] - y[i]) < 1.0e-10);
+ }
+
+ }
+
+ @Test
+ public void testImpossibleSerialization()
+ throws IOException {
+
+ double[] y = { 0.0, 1.0, -2.0 };
+ AbstractStepInterpolator interpolator = new BadStepInterpolator(y, true);
+ interpolator.storeTime(0);
+ interpolator.shift();
+ interpolator.storeTime(1);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ try {
+ oos.writeObject(interpolator);
+ fail("an exception should have been thrown");
+ } catch (IOException ioe) {
+ // expected behavior
+ assertEquals(0, ioe.getMessage().length());
+ }
+
+ }
+
+ private static class BadStepInterpolator extends DummyStepInterpolator {
+ @SuppressWarnings("unused")
+ public BadStepInterpolator() {
+ }
+ public BadStepInterpolator(double[] y, boolean forward) {
+ super(y, new double[y.length], forward);
+ }
+ @Override
+ protected void doFinalize() throws DerivativeException {
+ throw new DerivativeException((Localizable) null, LocalizedFormats.SIMPLE_MESSAGE, "");
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolatorTest.java b/src/test/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolatorTest.java
new file mode 100644
index 0000000..d27ea0a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/sampling/NordsieckStepInterpolatorTest.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.ode.sampling;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.ContinuousOutputModel;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem1;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.nonstiff.AdamsBashforthIntegrator;
+import org.junit.Test;
+
+public class NordsieckStepInterpolatorTest {
+
+ @Test
+ public void derivativesConsistency()
+ throws DerivativeException, IntegratorException {
+ TestProblem3 pb = new TestProblem3();
+ AdamsBashforthIntegrator integ = new AdamsBashforthIntegrator(4, 0.0, 1.0, 1.0e-10, 1.0e-10);
+ StepInterpolatorTestUtils.checkDerivativesConsistency(integ, pb, 5e-9);
+ }
+
+ @Test
+ public void serialization()
+ throws DerivativeException, IntegratorException,
+ IOException, ClassNotFoundException {
+
+ TestProblem1 pb = new TestProblem1();
+ AdamsBashforthIntegrator integ = new AdamsBashforthIntegrator(4, 0.0, 1.0, 1.0e-10, 1.0e-10);
+ integ.addStepHandler(new ContinuousOutputModel());
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ for (StepHandler handler : integ.getStepHandlers()) {
+ oos.writeObject(handler);
+ }
+
+ assertTrue(bos.size () > 25500);
+ assertTrue(bos.size () < 26500);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ ContinuousOutputModel cm = (ContinuousOutputModel) ois.readObject();
+
+ Random random = new Random(347588535632l);
+ double maxError = 0.0;
+ for (int i = 0; i < 1000; ++i) {
+ double r = random.nextDouble();
+ double time = r * pb.getInitialTime() + (1.0 - r) * pb.getFinalTime();
+ cm.setInterpolatedTime(time);
+ double[] interpolatedY = cm.getInterpolatedState ();
+ double[] theoreticalY = pb.computeTheoreticalState(time);
+ double dx = interpolatedY[0] - theoreticalY[0];
+ double dy = interpolatedY[1] - theoreticalY[1];
+ double error = dx * dx + dy * dy;
+ if (error > maxError) {
+ maxError = error;
+ }
+ }
+
+ assertTrue(maxError < 1.0e-6);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/ode/sampling/StepInterpolatorTestUtils.java b/src/test/java/org/apache/commons/math/ode/sampling/StepInterpolatorTestUtils.java
new file mode 100644
index 0000000..a0ca741
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/sampling/StepInterpolatorTestUtils.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.ode.sampling;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblemAbstract;
+import org.apache.commons.math.util.FastMath;
+
+public class StepInterpolatorTestUtils {
+
+ public static void checkDerivativesConsistency(final FirstOrderIntegrator integrator,
+ final TestProblemAbstract problem,
+ final double threshold)
+ throws DerivativeException, IntegratorException {
+ integrator.addStepHandler(new StepHandler() {
+
+ public boolean requiresDenseOutput() {
+ return true;
+ }
+
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws DerivativeException {
+
+ final double h = 0.001 * (interpolator.getCurrentTime() - interpolator.getPreviousTime());
+ final double t = interpolator.getCurrentTime() - 300 * h;
+
+ if (FastMath.abs(h) < 10 * FastMath.ulp(t)) {
+ return;
+ }
+
+ interpolator.setInterpolatedTime(t - 4 * h);
+ final double[] yM4h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t - 3 * h);
+ final double[] yM3h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t - 2 * h);
+ final double[] yM2h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t - h);
+ final double[] yM1h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t + h);
+ final double[] yP1h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t + 2 * h);
+ final double[] yP2h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t + 3 * h);
+ final double[] yP3h = interpolator.getInterpolatedState().clone();
+ interpolator.setInterpolatedTime(t + 4 * h);
+ final double[] yP4h = interpolator.getInterpolatedState().clone();
+
+ interpolator.setInterpolatedTime(t);
+ final double[] yDot = interpolator.getInterpolatedDerivatives();
+
+ for (int i = 0; i < yDot.length; ++i) {
+ final double approYDot = ( -3 * (yP4h[i] - yM4h[i]) +
+ 32 * (yP3h[i] - yM3h[i]) +
+ -168 * (yP2h[i] - yM2h[i]) +
+ 672 * (yP1h[i] - yM1h[i])) / (840 * h);
+ assertEquals(approYDot, yDot[i], threshold);
+ }
+
+ }
+
+ public void reset() {
+ }
+
+ });
+
+ integrator.integrate(problem,
+ problem.getInitialTime(), problem.getInitialState(),
+ problem.getFinalTime(), new double[problem.getDimension()]);
+
+ }
+}
+
diff --git a/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerTest.java b/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerTest.java
new file mode 100644
index 0000000..0f0f74b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerTest.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.sampling;
+
+import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.TestProblem3;
+import org.apache.commons.math.ode.nonstiff.DormandPrince54Integrator;
+import org.apache.commons.math.ode.sampling.FixedStepHandler;
+import org.apache.commons.math.ode.sampling.StepNormalizer;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.*;
+
+public class StepNormalizerTest
+ extends TestCase {
+
+ public StepNormalizerTest(String name) {
+ super(name);
+ pb = null;
+ integ = null;
+ }
+
+ public void testBoundaries()
+ throws DerivativeException, IntegratorException {
+ double range = pb.getFinalTime() - pb.getInitialTime();
+ setLastSeen(false);
+ integ.addStepHandler(new StepNormalizer(range / 10.0,
+ new FixedStepHandler() {
+ private static final long serialVersionUID = 1650337364641626444L;
+ private boolean firstCall = true;
+ public void handleStep(double t,
+ double[] y,
+ double[] yDot,
+ boolean isLast) {
+ if (firstCall) {
+ checkValue(t, pb.getInitialTime());
+ firstCall = false;
+ }
+ if (isLast) {
+ setLastSeen(true);
+ checkValue(t, pb.getFinalTime());
+ }
+ }
+ }));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertTrue(lastSeen);
+ }
+
+ public void testBeforeEnd()
+ throws DerivativeException, IntegratorException {
+ final double range = pb.getFinalTime() - pb.getInitialTime();
+ setLastSeen(false);
+ integ.addStepHandler(new StepNormalizer(range / 10.5,
+ new FixedStepHandler() {
+ private static final long serialVersionUID = 2228457391561277298L;
+ public void handleStep(double t,
+ double[] y,
+ double[] yDot,
+ boolean isLast) {
+ if (isLast) {
+ setLastSeen(true);
+ checkValue(t,
+ pb.getFinalTime() - range / 21.0);
+ }
+ }
+ }));
+ integ.integrate(pb,
+ pb.getInitialTime(), pb.getInitialState(),
+ pb.getFinalTime(), new double[pb.getDimension()]);
+ assertTrue(lastSeen);
+ }
+
+ public void checkValue(double value, double reference) {
+ assertTrue(FastMath.abs(value - reference) < 1.0e-10);
+ }
+
+ public void setLastSeen(boolean lastSeen) {
+ this.lastSeen = lastSeen;
+ }
+
+ @Override
+ public void setUp() {
+ pb = new TestProblem3(0.9);
+ double minStep = 0;
+ double maxStep = pb.getFinalTime() - pb.getInitialTime();
+ integ = new DormandPrince54Integrator(minStep, maxStep, 10.e-8, 1.0e-8);
+ lastSeen = false;
+ }
+
+ @Override
+ public void tearDown() {
+ pb = null;
+ integ = null;
+ }
+
+ TestProblem3 pb;
+ FirstOrderIntegrator integ;
+ boolean lastSeen;
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizerTest.java
new file mode 100644
index 0000000..72ec730
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateRealOptimizerTest.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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateVectorialFunction;
+import org.apache.commons.math.analysis.solvers.BrentSolver;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.general.ConjugateGradientFormula;
+import org.apache.commons.math.optimization.general.NonLinearConjugateGradientOptimizer;
+import org.apache.commons.math.random.GaussianRandomGenerator;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.random.RandomVectorGenerator;
+import org.apache.commons.math.random.UncorrelatedRandomVectorGenerator;
+import org.junit.Test;
+
+public class MultiStartDifferentiableMultivariateRealOptimizerTest {
+
+ @Test
+ public void testCircleFitting() throws Exception {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ NonLinearConjugateGradientOptimizer underlying =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(753289573253l);
+ RandomVectorGenerator generator =
+ new UncorrelatedRandomVectorGenerator(new double[] { 50.0, 50.0 }, new double[] { 10.0, 10.0 },
+ new GaussianRandomGenerator(g));
+ MultiStartDifferentiableMultivariateRealOptimizer optimizer =
+ new MultiStartDifferentiableMultivariateRealOptimizer(underlying, 10, generator);
+ optimizer.setMaxIterations(100);
+ assertEquals(100, optimizer.getMaxIterations());
+ optimizer.setMaxEvaluations(100);
+ assertEquals(100, optimizer.getMaxEvaluations());
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-10, 1.0e-10));
+ BrentSolver solver = new BrentSolver();
+ solver.setAbsoluteAccuracy(1.0e-13);
+ solver.setRelativeAccuracy(1.0e-15);
+ RealPointValuePair optimum =
+ optimizer.optimize(circle, GoalType.MINIMIZE, new double[] { 98.680, 47.345 });
+ RealPointValuePair[] optima = optimizer.getOptima();
+ for (RealPointValuePair o : optima) {
+ Point2D.Double center = new Point2D.Double(o.getPointRef()[0], o.getPointRef()[1]);
+ assertEquals(69.960161753, circle.getRadius(center), 1.0e-8);
+ assertEquals(96.075902096, center.x, 1.0e-8);
+ assertEquals(48.135167894, center.y, 1.0e-8);
+ }
+ assertTrue(optimizer.getGradientEvaluations() > 650);
+ assertTrue(optimizer.getGradientEvaluations() < 700);
+ assertTrue(optimizer.getEvaluations() > 70);
+ assertTrue(optimizer.getEvaluations() < 90);
+ assertTrue(optimizer.getIterations() > 70);
+ assertTrue(optimizer.getIterations() < 90);
+ assertEquals(3.1267527, optimum.getValue(), 1.0e-8);
+ }
+
+ private static class Circle implements DifferentiableMultivariateRealFunction {
+
+ private ArrayList<Point2D.Double> points;
+
+ public Circle() {
+ points = new ArrayList<Point2D.Double>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new Point2D.Double(px, py));
+ }
+
+ public double getRadius(Point2D.Double center) {
+ double r = 0;
+ for (Point2D.Double point : points) {
+ r += point.distance(center);
+ }
+ return r / points.size();
+ }
+
+ private double[] gradient(double[] point) {
+
+ // optimal radius
+ Point2D.Double center = new Point2D.Double(point[0], point[1]);
+ double radius = getRadius(center);
+
+ // gradient of the sum of squared residuals
+ double dJdX = 0;
+ double dJdY = 0;
+ for (Point2D.Double pk : points) {
+ double dk = pk.distance(center);
+ dJdX += (center.x - pk.x) * (dk - radius) / dk;
+ dJdY += (center.y - pk.y) * (dk - radius) / dk;
+ }
+ dJdX *= 2;
+ dJdY *= 2;
+
+ return new double[] { dJdX, dJdY };
+
+ }
+
+ public double value(double[] variables)
+ throws IllegalArgumentException, FunctionEvaluationException {
+
+ Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
+ double radius = getRadius(center);
+
+ double sum = 0;
+ for (Point2D.Double point : points) {
+ double di = point.distance(center) - radius;
+ sum += di * di;
+ }
+
+ return sum;
+
+ }
+
+ public MultivariateVectorialFunction gradient() {
+ return new MultivariateVectorialFunction() {
+ public double[] value(double[] point) {
+ return gradient(point);
+ }
+ };
+ }
+
+ public MultivariateRealFunction partialDerivative(final int k) {
+ return new MultivariateRealFunction() {
+ public double value(double[] point) {
+ return gradient(point)[k];
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizerTest.java
new file mode 100644
index 0000000..38ad2af
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/MultiStartDifferentiableMultivariateVectorialOptimizerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.Serializable;
+
+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.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
+import org.apache.commons.math.random.GaussianRandomGenerator;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.random.RandomVectorGenerator;
+import org.apache.commons.math.random.UncorrelatedRandomVectorGenerator;
+import org.junit.Test;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+public class MultiStartDifferentiableMultivariateVectorialOptimizerTest {
+
+ @Test
+ public void testTrivial() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 2 } }, new double[] { 3 });
+ DifferentiableMultivariateVectorialOptimizer underlyingOptimizer =
+ new GaussNewtonOptimizer(true);
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(16069223052l);
+ RandomVectorGenerator generator =
+ new UncorrelatedRandomVectorGenerator(1, new GaussianRandomGenerator(g));
+ MultiStartDifferentiableMultivariateVectorialOptimizer optimizer =
+ new MultiStartDifferentiableMultivariateVectorialOptimizer(underlyingOptimizer,
+ 10, generator);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+
+ // no optima before first optimization attempt
+ try {
+ optimizer.getOptima();
+ fail("an exception should have been thrown");
+ } catch (IllegalStateException ise) {
+ // expected
+ }
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1 }, new double[] { 0 });
+ assertEquals(1.5, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getValue()[0], 1.0e-10);
+ VectorialPointValuePair[] optima = optimizer.getOptima();
+ assertEquals(10, optima.length);
+ for (int i = 0; i < optima.length; ++i) {
+ assertEquals(1.5, optima[i].getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optima[i].getValue()[0], 1.0e-10);
+ }
+ assertTrue(optimizer.getEvaluations() > 20);
+ assertTrue(optimizer.getEvaluations() < 50);
+ assertTrue(optimizer.getIterations() > 20);
+ assertTrue(optimizer.getIterations() < 50);
+ assertTrue(optimizer.getJacobianEvaluations() > 20);
+ assertTrue(optimizer.getJacobianEvaluations() < 50);
+ assertEquals(100, optimizer.getMaxIterations());
+ }
+
+ @Test(expected = OptimizationException.class)
+ public void testNoOptimum() throws FunctionEvaluationException, OptimizationException {
+ DifferentiableMultivariateVectorialOptimizer underlyingOptimizer =
+ new GaussNewtonOptimizer(true);
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(12373523445l);
+ RandomVectorGenerator generator =
+ new UncorrelatedRandomVectorGenerator(1, new GaussianRandomGenerator(g));
+ MultiStartDifferentiableMultivariateVectorialOptimizer optimizer =
+ new MultiStartDifferentiableMultivariateVectorialOptimizer(underlyingOptimizer,
+ 10, generator);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ optimizer.optimize(new DifferentiableMultivariateVectorialFunction() {
+ public MultivariateMatrixFunction jacobian() {
+ return null;
+ }
+ public double[] value(double[] point) throws FunctionEvaluationException {
+ throw new FunctionEvaluationException(point);
+ }
+ }, new double[] { 2 }, new double[] { 1 }, new double[] { 0 });
+ }
+
+ private static class LinearProblem implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = -8804268799379350190L;
+ final RealMatrix factors;
+ final double[] target;
+ public LinearProblem(double[][] factors, double[] target) {
+ this.factors = new BlockRealMatrix(factors);
+ this.target = target;
+ }
+
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -8387467946663627585L;
+ public double[][] value(double[] point) {
+ return factors.getData();
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizerTest.java
new file mode 100644
index 0000000..97176a4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/MultiStartMultivariateRealOptimizerTest.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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.direct.NelderMead;
+import org.apache.commons.math.random.GaussianRandomGenerator;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.random.RandomVectorGenerator;
+import org.apache.commons.math.random.UncorrelatedRandomVectorGenerator;
+import org.junit.Test;
+
+public class MultiStartMultivariateRealOptimizerTest {
+
+ @Test
+ public void testRosenbrock() throws Exception {
+
+ Rosenbrock rosenbrock = new Rosenbrock();
+ NelderMead underlying = new NelderMead();
+ underlying.setStartConfiguration(new double[][] {
+ { -1.2, 1.0 }, { 0.9, 1.2 } , { 3.5, -2.3 }
+ });
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(16069223052l);
+ RandomVectorGenerator generator =
+ new UncorrelatedRandomVectorGenerator(2, new GaussianRandomGenerator(g));
+ MultiStartMultivariateRealOptimizer optimizer =
+ new MultiStartMultivariateRealOptimizer(underlying, 10, generator);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1, 1.0e-3));
+ optimizer.setMaxIterations(100);
+ RealPointValuePair optimum =
+ optimizer.optimize(rosenbrock, GoalType.MINIMIZE, new double[] { -1.2, 1.0 });
+
+ assertEquals(rosenbrock.getCount(), optimizer.getEvaluations());
+ assertTrue(optimizer.getEvaluations() > 20);
+ assertTrue(optimizer.getEvaluations() < 250);
+ assertTrue(optimum.getValue() < 8.0e-4);
+
+ }
+
+ private static class Rosenbrock implements MultivariateRealFunction {
+
+ private int count;
+
+ public Rosenbrock() {
+ count = 0;
+ }
+
+ public double value(double[] x) throws FunctionEvaluationException {
+ ++count;
+ double a = x[1] - x[0] * x[0];
+ double b = 1.0 - x[0];
+ return 100 * a * a + b * b;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java
new file mode 100644
index 0000000..df5403c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/MultiStartUnivariateRealOptimizerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.optimization.univariate.BrentOptimizer;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class MultiStartUnivariateRealOptimizerTest {
+
+ @Test
+ public void testSinMin() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealOptimizer underlying = new BrentOptimizer();
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(44428400075l);
+ MultiStartUnivariateRealOptimizer minimizer =
+ new MultiStartUnivariateRealOptimizer(underlying, 10, g);
+ minimizer.optimize(f, GoalType.MINIMIZE, -100.0, 100.0);
+ double[] optima = minimizer.getOptima();
+ double[] optimaValues = minimizer.getOptimaValues();
+ for (int i = 1; i < optima.length; ++i) {
+ double d = (optima[i] - optima[i-1]) / (2 * FastMath.PI);
+ assertTrue (FastMath.abs(d - FastMath.rint(d)) < 1.0e-8);
+ assertEquals(-1.0, f.value(optima[i]), 1.0e-10);
+ assertEquals(f.value(optima[i]), optimaValues[i], 1.0e-10);
+ }
+ assertTrue(minimizer.getEvaluations() > 150);
+ assertTrue(minimizer.getEvaluations() < 250);
+ }
+
+ @Test
+ public void testQuinticMin() throws MathException {
+ // The quintic function has zeros at 0, +-0.5 and +-1.
+ // The function has extrema (first derivative is zero) at 0.27195613 and 0.82221643,
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealOptimizer underlying = new BrentOptimizer();
+ underlying.setRelativeAccuracy(1e-15);
+ JDKRandomGenerator g = new JDKRandomGenerator();
+ g.setSeed(4312000053L);
+ MultiStartUnivariateRealOptimizer minimizer =
+ new MultiStartUnivariateRealOptimizer(underlying, 5, g);
+ minimizer.setAbsoluteAccuracy(10 * minimizer.getAbsoluteAccuracy());
+ minimizer.setRelativeAccuracy(10 * minimizer.getRelativeAccuracy());
+
+ try {
+ minimizer.getOptima();
+ fail("an exception should have been thrown");
+ } catch (IllegalStateException ise) {
+ // expected
+ } catch (Exception e) {
+ fail("wrong exception caught");
+ }
+ try {
+ minimizer.getOptimaValues();
+ fail("an exception should have been thrown");
+ } catch (IllegalStateException ise) {
+ // expected
+ } catch (Exception e) {
+ fail("wrong exception caught");
+ }
+
+ double result = minimizer.optimize(f, GoalType.MINIMIZE, -0.3, -0.2);
+ assertEquals(-0.2719561270319131, result, 1.0e-13);
+ assertEquals(-0.2719561270319131, minimizer.getResult(), 1.0e-13);
+ assertEquals(-0.04433426954946637, minimizer.getFunctionValue(), 1.0e-13);
+
+ double[] optima = minimizer.getOptima();
+ double[] optimaValues = minimizer.getOptimaValues();
+ for (int i = 0; i < optima.length; ++i) {
+ assertEquals(f.value(optima[i]), optimaValues[i], 1.0e-10);
+ }
+ assertTrue(minimizer.getEvaluations() >= 120);
+ assertTrue(minimizer.getEvaluations() <= 170);
+ assertTrue(minimizer.getIterationCount() >= 120);
+ assertTrue(minimizer.getIterationCount() <= 170);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/direct/MultiDirectionalTest.java b/src/test/java/org/apache/commons/math/optimization/direct/MultiDirectionalTest.java
new file mode 100644
index 0000000..1400727
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/direct/MultiDirectionalTest.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.direct;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+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.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MultiDirectionalTest {
+
+ @Test
+ public void testFunctionEvaluationException() throws OptimizationException, FunctionEvaluationException, IllegalArgumentException {
+ MultivariateRealFunction wrong =
+ new MultivariateRealFunction() {
+ private static final long serialVersionUID = 4751314470965489371L;
+ public double value(double[] x) throws FunctionEvaluationException {
+ if (x[0] < 0) {
+ throw new FunctionEvaluationException(x,LocalizedFormats.SIMPLE_MESSAGE, "oops");
+ } else if (x[0] > 1) {
+ throw (new FunctionEvaluationException(new RuntimeException("oops"), x));
+ } else {
+ return x[0] * (1 - x[0]);
+ }
+ }
+ };
+ try {
+ MultiDirectional optimizer = new MultiDirectional(0.9, 1.9);
+ optimizer.optimize(wrong, GoalType.MINIMIZE, new double[] { -1.0 });
+ Assert.fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException ce) {
+ // expected behavior
+ Assert.assertNull(ce.getCause());
+ }
+ try {
+ MultiDirectional optimizer = new MultiDirectional(0.9, 1.9);
+ optimizer.optimize(wrong, GoalType.MINIMIZE, new double[] { +2.0 });
+ Assert.fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException ce) {
+ // expected behavior
+ Assert.assertNotNull(ce.getCause());
+ }
+ }
+
+ @Test
+ public void testMinimizeMaximize()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ // the following function has 4 local extrema:
+ final double xM = -3.841947088256863675365;
+ final double yM = -1.391745200270734924416;
+ final double xP = 0.2286682237349059125691;
+ final double yP = -yM;
+ final double valueXmYm = 0.2373295333134216789769; // local maximum
+ final double valueXmYp = -valueXmYm; // local minimum
+ final double valueXpYm = -0.7290400707055187115322; // global minimum
+ final double valueXpYp = -valueXpYm; // global maximum
+ MultivariateRealFunction fourExtrema = new MultivariateRealFunction() {
+ private static final long serialVersionUID = -7039124064449091152L;
+ public double value(double[] variables) throws FunctionEvaluationException {
+ final double x = variables[0];
+ final double y = variables[1];
+ return ((x == 0) || (y == 0)) ? 0 : (FastMath.atan(x) * FastMath.atan(x + 2) * FastMath.atan(y) * FastMath.atan(y) / (x * y));
+ }
+ };
+
+ MultiDirectional optimizer = new MultiDirectional();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-11, 1.0e-30));
+ optimizer.setMaxIterations(200);
+ optimizer.setStartConfiguration(new double[] { 0.2, 0.2 });
+ RealPointValuePair optimum;
+
+ // minimization
+ optimum = optimizer.optimize(fourExtrema, GoalType.MINIMIZE, new double[] { -3.0, 0 });
+ Assert.assertEquals(xM, optimum.getPoint()[0], 4.0e-6);
+ Assert.assertEquals(yP, optimum.getPoint()[1], 3.0e-6);
+ Assert.assertEquals(valueXmYp, optimum.getValue(), 8.0e-13);
+ Assert.assertTrue(optimizer.getEvaluations() > 120);
+ Assert.assertTrue(optimizer.getEvaluations() < 150);
+
+ optimum = optimizer.optimize(fourExtrema, GoalType.MINIMIZE, new double[] { +1, 0 });
+ Assert.assertEquals(xP, optimum.getPoint()[0], 2.0e-8);
+ Assert.assertEquals(yM, optimum.getPoint()[1], 3.0e-6);
+ Assert.assertEquals(valueXpYm, optimum.getValue(), 2.0e-12);
+ Assert.assertTrue(optimizer.getEvaluations() > 120);
+ Assert.assertTrue(optimizer.getEvaluations() < 150);
+
+ // maximization
+ optimum = optimizer.optimize(fourExtrema, GoalType.MAXIMIZE, new double[] { -3.0, 0.0 });
+ Assert.assertEquals(xM, optimum.getPoint()[0], 7.0e-7);
+ Assert.assertEquals(yM, optimum.getPoint()[1], 3.0e-7);
+ Assert.assertEquals(valueXmYm, optimum.getValue(), 2.0e-14);
+ Assert.assertTrue(optimizer.getEvaluations() > 120);
+ Assert.assertTrue(optimizer.getEvaluations() < 150);
+
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-15, 1.0e-30));
+ optimum = optimizer.optimize(fourExtrema, GoalType.MAXIMIZE, new double[] { +1, 0 });
+ Assert.assertEquals(xP, optimum.getPoint()[0], 2.0e-8);
+ Assert.assertEquals(yP, optimum.getPoint()[1], 3.0e-6);
+ Assert.assertEquals(valueXpYp, optimum.getValue(), 2.0e-12);
+ Assert.assertTrue(optimizer.getEvaluations() > 180);
+ Assert.assertTrue(optimizer.getEvaluations() < 220);
+
+ }
+
+ @Test
+ public void testRosenbrock()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ MultivariateRealFunction rosenbrock =
+ new MultivariateRealFunction() {
+ private static final long serialVersionUID = -9044950469615237490L;
+ public double value(double[] x) throws FunctionEvaluationException {
+ ++count;
+ double a = x[1] - x[0] * x[0];
+ double b = 1.0 - x[0];
+ return 100 * a * a + b * b;
+ }
+ };
+
+ count = 0;
+ MultiDirectional optimizer = new MultiDirectional();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1, 1.0e-3));
+ optimizer.setMaxIterations(100);
+ optimizer.setStartConfiguration(new double[][] {
+ { -1.2, 1.0 }, { 0.9, 1.2 } , { 3.5, -2.3 }
+ });
+ RealPointValuePair optimum =
+ optimizer.optimize(rosenbrock, GoalType.MINIMIZE, new double[] { -1.2, 1.0 });
+
+ Assert.assertEquals(count, optimizer.getEvaluations());
+ Assert.assertTrue(optimizer.getEvaluations() > 50);
+ Assert.assertTrue(optimizer.getEvaluations() < 100);
+ Assert.assertTrue(optimum.getValue() > 1.0e-2);
+
+ }
+
+ @Test
+ public void testPowell()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ MultivariateRealFunction powell =
+ new MultivariateRealFunction() {
+ private static final long serialVersionUID = -832162886102041840L;
+ public double value(double[] x) throws FunctionEvaluationException {
+ ++count;
+ double a = x[0] + 10 * x[1];
+ double b = x[2] - x[3];
+ double c = x[1] - 2 * x[2];
+ double d = x[0] - x[3];
+ return a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d;
+ }
+ };
+
+ count = 0;
+ MultiDirectional optimizer = new MultiDirectional();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-3));
+ optimizer.setMaxIterations(1000);
+ RealPointValuePair optimum =
+ optimizer.optimize(powell, GoalType.MINIMIZE, new double[] { 3.0, -1.0, 0.0, 1.0 });
+ Assert.assertEquals(count, optimizer.getEvaluations());
+ Assert.assertTrue(optimizer.getEvaluations() > 800);
+ Assert.assertTrue(optimizer.getEvaluations() < 900);
+ Assert.assertTrue(optimum.getValue() > 1.0e-2);
+
+ }
+
+ @Test
+ public void testMath283()
+ throws FunctionEvaluationException, OptimizationException {
+ // fails because MultiDirectional.iterateSimplex is looping forever
+ // the while(true) should be replaced with a convergence check
+ MultiDirectional multiDirectional = new MultiDirectional();
+ multiDirectional.setMaxIterations(100);
+ multiDirectional.setMaxEvaluations(1000);
+
+ final Gaussian2D function = new Gaussian2D(0.0, 0.0, 1.0);
+
+ RealPointValuePair estimate = multiDirectional.optimize(function,
+ GoalType.MAXIMIZE, function.getMaximumPosition());
+
+ final double EPSILON = 1e-5;
+
+ final double expectedMaximum = function.getMaximum();
+ final double actualMaximum = estimate.getValue();
+ Assert.assertEquals(expectedMaximum, actualMaximum, EPSILON);
+
+ final double[] expectedPosition = function.getMaximumPosition();
+ final double[] actualPosition = estimate.getPoint();
+ Assert.assertEquals(expectedPosition[0], actualPosition[0], EPSILON );
+ Assert.assertEquals(expectedPosition[1], actualPosition[1], EPSILON );
+
+ }
+
+ private static class Gaussian2D implements MultivariateRealFunction {
+
+ private final double[] maximumPosition;
+
+ private final double std;
+
+ public Gaussian2D(double xOpt, double yOpt, double std) {
+ maximumPosition = new double[] { xOpt, yOpt };
+ this.std = std;
+ }
+
+ public double getMaximum() {
+ return value(maximumPosition);
+ }
+
+ public double[] getMaximumPosition() {
+ return maximumPosition.clone();
+ }
+
+ public double value(double[] point) {
+ final double x = point[0], y = point[1];
+ final double twoS2 = 2.0 * std * std;
+ return 1.0 / (twoS2 * FastMath.PI) * FastMath.exp(-(x * x + y * y) / twoS2);
+ }
+ }
+
+ private int count;
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/direct/NelderMeadTest.java b/src/test/java/org/apache/commons/math/optimization/direct/NelderMeadTest.java
new file mode 100644
index 0000000..e8ddc1d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/direct/NelderMeadTest.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.direct;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.commons.math.ConvergenceException;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MaxEvaluationsExceededException;
+import org.apache.commons.math.MaxIterationsExceededException;
+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.Array2DRowRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.LeastSquaresConverter;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleRealPointChecker;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class NelderMeadTest {
+
+ @Test
+ public void testFunctionEvaluationException() throws OptimizationException, FunctionEvaluationException, IllegalArgumentException {
+ MultivariateRealFunction wrong =
+ new MultivariateRealFunction() {
+ private static final long serialVersionUID = 4751314470965489371L;
+ public double value(double[] x) throws FunctionEvaluationException {
+ if (x[0] < 0) {
+ throw new FunctionEvaluationException(x, LocalizedFormats.SIMPLE_MESSAGE, "oops");
+ } else if (x[0] > 1) {
+ throw new FunctionEvaluationException(new RuntimeException("oops"), x);
+ } else {
+ return x[0] * (1 - x[0]);
+ }
+ }
+ };
+ try {
+ NelderMead optimizer = new NelderMead(0.9, 1.9, 0.4, 0.6);
+ optimizer.optimize(wrong, GoalType.MINIMIZE, new double[] { -1.0 });
+ fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException ce) {
+ // expected behavior
+ assertNull(ce.getCause());
+ }
+ try {
+ NelderMead optimizer = new NelderMead(0.9, 1.9, 0.4, 0.6);
+ optimizer.optimize(wrong, GoalType.MINIMIZE, new double[] { +2.0 });
+ fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException ce) {
+ // expected behavior
+ assertNotNull(ce.getCause());
+ }
+ }
+
+ @Test
+ public void testMinimizeMaximize()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ // the following function has 4 local extrema:
+ final double xM = -3.841947088256863675365;
+ final double yM = -1.391745200270734924416;
+ final double xP = 0.2286682237349059125691;
+ final double yP = -yM;
+ final double valueXmYm = 0.2373295333134216789769; // local maximum
+ final double valueXmYp = -valueXmYm; // local minimum
+ final double valueXpYm = -0.7290400707055187115322; // global minimum
+ final double valueXpYp = -valueXpYm; // global maximum
+ MultivariateRealFunction fourExtrema = new MultivariateRealFunction() {
+ private static final long serialVersionUID = -7039124064449091152L;
+ public double value(double[] variables) throws FunctionEvaluationException {
+ final double x = variables[0];
+ final double y = variables[1];
+ return ((x == 0) || (y == 0)) ? 0 : (FastMath.atan(x) * FastMath.atan(x + 2) * FastMath.atan(y) * FastMath.atan(y) / (x * y));
+ }
+ };
+
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-10, 1.0e-30));
+ optimizer.setMaxIterations(100);
+ optimizer.setStartConfiguration(new double[] { 0.2, 0.2 });
+ RealPointValuePair optimum;
+
+ // minimization
+ optimum = optimizer.optimize(fourExtrema, GoalType.MINIMIZE, new double[] { -3.0, 0 });
+ assertEquals(xM, optimum.getPoint()[0], 2.0e-7);
+ assertEquals(yP, optimum.getPoint()[1], 2.0e-5);
+ assertEquals(valueXmYp, optimum.getValue(), 6.0e-12);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 90);
+
+ optimum = optimizer.optimize(fourExtrema, GoalType.MINIMIZE, new double[] { +1, 0 });
+ assertEquals(xP, optimum.getPoint()[0], 5.0e-6);
+ assertEquals(yM, optimum.getPoint()[1], 6.0e-6);
+ assertEquals(valueXpYm, optimum.getValue(), 1.0e-11);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 90);
+
+ // maximization
+ optimum = optimizer.optimize(fourExtrema, GoalType.MAXIMIZE, new double[] { -3.0, 0.0 });
+ assertEquals(xM, optimum.getPoint()[0], 1.0e-5);
+ assertEquals(yM, optimum.getPoint()[1], 3.0e-6);
+ assertEquals(valueXmYm, optimum.getValue(), 3.0e-12);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 90);
+
+ optimum = optimizer.optimize(fourExtrema, GoalType.MAXIMIZE, new double[] { +1, 0 });
+ assertEquals(xP, optimum.getPoint()[0], 4.0e-6);
+ assertEquals(yP, optimum.getPoint()[1], 5.0e-6);
+ assertEquals(valueXpYp, optimum.getValue(), 7.0e-12);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 90);
+
+ }
+
+ @Test
+ public void testRosenbrock()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ Rosenbrock rosenbrock = new Rosenbrock();
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1, 1.0e-3));
+ optimizer.setMaxIterations(100);
+ optimizer.setStartConfiguration(new double[][] {
+ { -1.2, 1.0 }, { 0.9, 1.2 } , { 3.5, -2.3 }
+ });
+ RealPointValuePair optimum =
+ optimizer.optimize(rosenbrock, GoalType.MINIMIZE, new double[] { -1.2, 1.0 });
+
+ assertEquals(rosenbrock.getCount(), optimizer.getEvaluations());
+ assertTrue(optimizer.getEvaluations() > 40);
+ assertTrue(optimizer.getEvaluations() < 50);
+ assertTrue(optimum.getValue() < 8.0e-4);
+
+ }
+
+ @Test
+ public void testPowell()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ Powell powell = new Powell();
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-3));
+ optimizer.setMaxIterations(200);
+ RealPointValuePair optimum =
+ optimizer.optimize(powell, GoalType.MINIMIZE, new double[] { 3.0, -1.0, 0.0, 1.0 });
+ assertEquals(powell.getCount(), optimizer.getEvaluations());
+ assertTrue(optimizer.getEvaluations() > 110);
+ assertTrue(optimizer.getEvaluations() < 130);
+ assertTrue(optimum.getValue() < 2.0e-3);
+
+ }
+
+ @Test
+ public void testLeastSquares1()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ final RealMatrix factors =
+ new Array2DRowRealMatrix(new double[][] {
+ { 1.0, 0.0 },
+ { 0.0, 1.0 }
+ }, false);
+ LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorialFunction() {
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+ }, new double[] { 2.0, -3.0 });
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-6));
+ optimizer.setMaxIterations(200);
+ RealPointValuePair optimum =
+ optimizer.optimize(ls, GoalType.MINIMIZE, new double[] { 10.0, 10.0 });
+ assertEquals( 2.0, optimum.getPointRef()[0], 3.0e-5);
+ assertEquals(-3.0, optimum.getPointRef()[1], 4.0e-4);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 80);
+ assertTrue(optimum.getValue() < 1.0e-6);
+ }
+
+ @Test
+ public void testLeastSquares2()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ final RealMatrix factors =
+ new Array2DRowRealMatrix(new double[][] {
+ { 1.0, 0.0 },
+ { 0.0, 1.0 }
+ }, false);
+ LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorialFunction() {
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+ }, new double[] { 2.0, -3.0 }, new double[] { 10.0, 0.1 });
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-6));
+ optimizer.setMaxIterations(200);
+ RealPointValuePair optimum =
+ optimizer.optimize(ls, GoalType.MINIMIZE, new double[] { 10.0, 10.0 });
+ assertEquals( 2.0, optimum.getPointRef()[0], 5.0e-5);
+ assertEquals(-3.0, optimum.getPointRef()[1], 8.0e-4);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 80);
+ assertTrue(optimum.getValue() < 1.0e-6);
+ }
+
+ @Test
+ public void testLeastSquares3()
+ throws FunctionEvaluationException, ConvergenceException {
+
+ final RealMatrix factors =
+ new Array2DRowRealMatrix(new double[][] {
+ { 1.0, 0.0 },
+ { 0.0, 1.0 }
+ }, false);
+ LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorialFunction() {
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+ }, new double[] { 2.0, -3.0 }, new Array2DRowRealMatrix(new double [][] {
+ { 1.0, 1.2 }, { 1.2, 2.0 }
+ }));
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-6));
+ optimizer.setMaxIterations(200);
+ RealPointValuePair optimum =
+ optimizer.optimize(ls, GoalType.MINIMIZE, new double[] { 10.0, 10.0 });
+ assertEquals( 2.0, optimum.getPointRef()[0], 2.0e-3);
+ assertEquals(-3.0, optimum.getPointRef()[1], 8.0e-4);
+ assertTrue(optimizer.getEvaluations() > 60);
+ assertTrue(optimizer.getEvaluations() < 80);
+ assertTrue(optimum.getValue() < 1.0e-6);
+ }
+
+ @Test(expected = MaxIterationsExceededException.class)
+ public void testMaxIterations() throws MathException {
+ try {
+ Powell powell = new Powell();
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(-1.0, 1.0e-3));
+ optimizer.setMaxIterations(20);
+ optimizer.optimize(powell, GoalType.MINIMIZE, new double[] { 3.0, -1.0, 0.0, 1.0 });
+ } catch (OptimizationException oe) {
+ if (oe.getCause() instanceof ConvergenceException) {
+ throw (ConvergenceException) oe.getCause();
+ }
+ throw oe;
+ }
+ }
+
+ @Test(expected = MaxEvaluationsExceededException.class)
+ public void testMaxEvaluations() throws MathException {
+ try {
+ Powell powell = new Powell();
+ NelderMead optimizer = new NelderMead();
+ optimizer.setConvergenceChecker(new SimpleRealPointChecker(-1.0, 1.0e-3));
+ optimizer.setMaxEvaluations(20);
+ optimizer.optimize(powell, GoalType.MINIMIZE, new double[] { 3.0, -1.0, 0.0, 1.0 });
+ } catch (FunctionEvaluationException fee) {
+ if (fee.getCause() instanceof ConvergenceException) {
+ throw (ConvergenceException) fee.getCause();
+ }
+ throw fee;
+ }
+ }
+
+ private static class Rosenbrock implements MultivariateRealFunction {
+
+ private int count;
+
+ public Rosenbrock() {
+ count = 0;
+ }
+
+ public double value(double[] x) {
+ ++count;
+ double a = x[1] - x[0] * x[0];
+ double b = 1.0 - x[0];
+ return 100 * a * a + b * b;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ }
+
+ private static class Powell implements MultivariateRealFunction {
+
+ private int count;
+
+ public Powell() {
+ count = 0;
+ }
+
+ public double value(double[] x) {
+ ++count;
+ double a = x[0] + 10 * x[1];
+ double b = x[2] - x[3];
+ double c = x[1] - 2 * x[2];
+ double d = x[0] - x[3];
+ return a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/direct/PowellOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/direct/PowellOptimizerTest.java
new file mode 100644
index 0000000..dfbde93
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/direct/PowellOptimizerTest.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.optimization.direct;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateVectorialFunction;
+import org.apache.commons.math.analysis.SumSincFunction;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link PowellOptimizer}.
+ */
+public class PowellOptimizerTest {
+
+ @Test
+ public void testSumSinc() throws MathException {
+ final DifferentiableMultivariateRealFunction func = new SumSincFunction(-1);
+
+ int dim = 2;
+ final double[] minPoint = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ minPoint[i] = 0;
+ }
+
+ double[] init = new double[dim];
+
+ // Initial is minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = minPoint[i];
+ }
+ // doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-5, 1e-9, 1e-7);
+
+ // Initial is far from minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = minPoint[i] + 3;
+ }
+ doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-5, 1e-9, 1e-7);
+ }
+
+ @Test
+ public void testQuadratic() throws MathException {
+ final DifferentiableMultivariateRealFunction func = new DifferentiableMultivariateRealFunction() {
+ public double value(double[] x) {
+ final double a = x[0] - 1;
+ final double b = x[1] - 1;
+ return a * a + b * b + 1;
+ }
+ public MultivariateRealFunction partialDerivative(int k) {
+ return null; // Not used
+ }
+ public MultivariateVectorialFunction gradient() {
+ return null; // Not used
+ }
+ };
+
+ int dim = 2;
+ final double[] minPoint = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ minPoint[i] = 1;
+ }
+
+ double[] init = new double[dim];
+
+ // Initial is minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = minPoint[i];
+ }
+ doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-5, 1e-9, 1e-8);
+
+ // Initial is far from minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = minPoint[i] - 20;
+ }
+ doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-5, 1e-9, 1e-8);
+ }
+
+ @Test
+ public void testMaximizeQuadratic() throws MathException {
+ final DifferentiableMultivariateRealFunction func = new DifferentiableMultivariateRealFunction() {
+ public double value(double[] x) {
+ final double a = x[0] - 1;
+ final double b = x[1] - 1;
+ return -a * a - b * b + 1;
+ }
+ public MultivariateRealFunction partialDerivative(int k) {
+ return null; // Not used
+ }
+ public MultivariateVectorialFunction gradient() {
+ return null; // Not used
+ }
+ };
+
+ int dim = 2;
+ final double[] maxPoint = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ maxPoint[i] = 1;
+ }
+
+ double[] init = new double[dim];
+
+ // Initial is minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = maxPoint[i];
+ }
+ doTest(func, maxPoint, init, GoalType.MAXIMIZE, 1e-5, 1e-9, 1e-8);
+
+ // Initial is far from minimum.
+ for (int i = 0; i < dim; i++) {
+ init[i] = maxPoint[i] - 20;
+ }
+ doTest(func, maxPoint, init, GoalType.MAXIMIZE, 1e-5, 1e-9, 1e-8);
+ }
+
+ /**
+ * @param func Function to optimize.
+ * @param optimum Expected optimum.
+ * @param init Starting point.
+ * @param goal Minimization or maximization.
+ * @param xTol Tolerance (relative error on the objective function) for
+ * "Brent" line search algorithm used by "Powell".
+ * @param fTol Tolerance (relative error on the objective function) for
+ * "Powell" algorithm.
+ * @param pointTol Tolerance for checking that the optimum is correct.
+ */
+ private void doTest(DifferentiableMultivariateRealFunction func,
+ double[] optimum,
+ double[] init,
+ GoalType goal,
+ double xTol,
+ double fTol,
+ double pointTol)
+ throws MathException {
+ final PowellOptimizer optim = new PowellOptimizer(xTol);
+ optim.setConvergenceChecker(new SimpleScalarValueChecker(fTol, -1));
+
+ final RealPointValuePair result = optim.optimize(func, goal, init);
+ final double[] found = result.getPoint();
+
+ for (int i = 0, dim = optimum.length; i < dim; i++) {
+ Assert.assertEquals(optimum[i], found[i], pointTol);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/fitting/CurveFitterTest.java b/src/test/java/org/apache/commons/math/optimization/fitting/CurveFitterTest.java
new file mode 100644
index 0000000..5604e86
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/fitting/CurveFitterTest.java
@@ -0,0 +1,156 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.commons.math.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CurveFitterTest {
+
+ @Test
+ public void testMath303()
+ throws OptimizationException, FunctionEvaluationException {
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ CurveFitter fitter = new CurveFitter(optimizer);
+ fitter.addObservedPoint(2.805d, 0.6934785852953367d);
+ fitter.addObservedPoint(2.74333333333333d, 0.6306772025518496d);
+ fitter.addObservedPoint(1.655d, 0.9474675497289684);
+ fitter.addObservedPoint(1.725d, 0.9013594835804194d);
+
+ ParametricRealFunction sif = new SimpleInverseFunction();
+
+ double[] initialguess1 = new double[1];
+ initialguess1[0] = 1.0d;
+ Assert.assertEquals(1, fitter.fit(sif, initialguess1).length);
+
+ double[] initialguess2 = new double[2];
+ initialguess2[0] = 1.0d;
+ initialguess2[1] = .5d;
+ Assert.assertEquals(2, fitter.fit(sif, initialguess2).length);
+
+ }
+
+ @Test
+ public void testMath304()
+ throws OptimizationException, FunctionEvaluationException {
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ CurveFitter fitter = new CurveFitter(optimizer);
+ fitter.addObservedPoint(2.805d, 0.6934785852953367d);
+ fitter.addObservedPoint(2.74333333333333d, 0.6306772025518496d);
+ fitter.addObservedPoint(1.655d, 0.9474675497289684);
+ fitter.addObservedPoint(1.725d, 0.9013594835804194d);
+
+ ParametricRealFunction sif = new SimpleInverseFunction();
+
+ double[] initialguess1 = new double[1];
+ initialguess1[0] = 1.0d;
+ Assert.assertEquals(1.6357215104109237, fitter.fit(sif, initialguess1)[0], 1.0e-14);
+
+ double[] initialguess2 = new double[1];
+ initialguess2[0] = 10.0d;
+ Assert.assertEquals(1.6357215104109237, fitter.fit(sif, initialguess1)[0], 1.0e-14);
+
+ }
+
+ @Test
+ public void testMath372()
+ throws OptimizationException, FunctionEvaluationException {
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ CurveFitter curveFitter = new CurveFitter(optimizer);
+
+ curveFitter.addObservedPoint( 15, 4443);
+ curveFitter.addObservedPoint( 31, 8493);
+ curveFitter.addObservedPoint( 62, 17586);
+ curveFitter.addObservedPoint(125, 30582);
+ curveFitter.addObservedPoint(250, 45087);
+ curveFitter.addObservedPoint(500, 50683);
+
+ ParametricRealFunction f = new ParametricRealFunction() {
+
+ public double value(double x, double[] parameters) {
+
+ double a = parameters[0];
+ double b = parameters[1];
+ double c = parameters[2];
+ double d = parameters[3];
+
+ return d + ((a - d) / (1 + FastMath.pow(x / c, b)));
+ }
+
+ public double[] gradient(double x, double[] parameters) {
+
+ double a = parameters[0];
+ double b = parameters[1];
+ double c = parameters[2];
+ double d = parameters[3];
+
+ double[] gradients = new double[4];
+ double den = 1 + FastMath.pow(x / c, b);
+
+ // derivative with respect to a
+ gradients[0] = 1 / den;
+
+ // derivative with respect to b
+ // in the reported (invalid) issue, there was a sign error here
+ gradients[1] = -((a - d) * FastMath.pow(x / c, b) * FastMath.log(x / c)) / (den * den);
+
+ // derivative with respect to c
+ gradients[2] = (b * FastMath.pow(x / c, b - 1) * (x / (c * c)) * (a - d)) / (den * den);
+
+ // derivative with respect to d
+ gradients[3] = 1 - (1 / den);
+
+ return gradients;
+
+ }
+ };
+
+ double[] initialGuess = new double[] { 1500, 0.95, 65, 35000 };
+ double[] estimatedParameters = curveFitter.fit(f, initialGuess);
+
+ Assert.assertEquals( 2411.00, estimatedParameters[0], 500.00);
+ Assert.assertEquals( 1.62, estimatedParameters[1], 0.04);
+ Assert.assertEquals( 111.22, estimatedParameters[2], 0.30);
+ Assert.assertEquals(55347.47, estimatedParameters[3], 300.00);
+ Assert.assertTrue(optimizer.getRMS() < 600.0);
+
+ }
+
+ private static class SimpleInverseFunction implements ParametricRealFunction {
+
+ public double value(double x, double[] parameters) {
+ return parameters[0] / x + (parameters.length < 2 ? 0 : parameters[1]);
+ }
+
+ public double[] gradient(double x, double[] doubles) {
+ double[] gradientVector = new double[doubles.length];
+ gradientVector[0] = 1 / x;
+ if (doubles.length >= 2) {
+ gradientVector[1] = 1;
+ }
+ return gradientVector;
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/fitting/GaussianFitterTest.java b/src/test/java/org/apache/commons/math/optimization/fitting/GaussianFitterTest.java
new file mode 100644
index 0000000..3dea090
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/fitting/GaussianFitterTest.java
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 static org.junit.Assert.assertEquals;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.general.
+ LevenbergMarquardtOptimizer;
+import org.junit.Test;
+
+/**
+ * Tests {@link GaussianFitter}.
+ *
+ * @since 2.2
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class GaussianFitterTest {
+ /** Good data. */
+ protected static final double[][] DATASET1 = new double[][] {
+ {4.0254623, 531026.0},
+ {4.02804905, 664002.0},
+ {4.02934242, 787079.0},
+ {4.03128248, 984167.0},
+ {4.03386923, 1294546.0},
+ {4.03580929, 1560230.0},
+ {4.03839603, 1887233.0},
+ {4.0396894, 2113240.0},
+ {4.04162946, 2375211.0},
+ {4.04421621, 2687152.0},
+ {4.04550958, 2862644.0},
+ {4.04744964, 3078898.0},
+ {4.05003639, 3327238.0},
+ {4.05132976, 3461228.0},
+ {4.05326982, 3580526.0},
+ {4.05585657, 3576946.0},
+ {4.05779662, 3439750.0},
+ {4.06038337, 3220296.0},
+ {4.06167674, 3070073.0},
+ {4.0636168, 2877648.0},
+ {4.06620355, 2595848.0},
+ {4.06749692, 2390157.0},
+ {4.06943698, 2175960.0},
+ {4.07202373, 1895104.0},
+ {4.0733171, 1687576.0},
+ {4.07525716, 1447024.0},
+ {4.0778439, 1130879.0},
+ {4.07978396, 904900.0},
+ {4.08237071, 717104.0},
+ {4.08366408, 620014.0}
+ };
+ /** Poor data: right of peak not symmetric with left of peak. */
+ protected static final double[][] DATASET2 = new double[][] {
+ {-20.15, 1523.0},
+ {-19.65, 1566.0},
+ {-19.15, 1592.0},
+ {-18.65, 1927.0},
+ {-18.15, 3089.0},
+ {-17.65, 6068.0},
+ {-17.15, 14239.0},
+ {-16.65, 34124.0},
+ {-16.15, 64097.0},
+ {-15.65, 110352.0},
+ {-15.15, 164742.0},
+ {-14.65, 209499.0},
+ {-14.15, 267274.0},
+ {-13.65, 283290.0},
+ {-13.15, 275363.0},
+ {-12.65, 258014.0},
+ {-12.15, 225000.0},
+ {-11.65, 200000.0},
+ {-11.15, 190000.0},
+ {-10.65, 185000.0},
+ {-10.15, 180000.0},
+ { -9.65, 179000.0},
+ { -9.15, 178000.0},
+ { -8.65, 177000.0},
+ { -8.15, 176000.0},
+ { -7.65, 175000.0},
+ { -7.15, 174000.0},
+ { -6.65, 173000.0},
+ { -6.15, 172000.0},
+ { -5.65, 171000.0},
+ { -5.15, 170000.0}
+ };
+ /** Poor data: long tails. */
+ protected static final double[][] DATASET3 = new double[][] {
+ {-90.15, 1513.0},
+ {-80.15, 1514.0},
+ {-70.15, 1513.0},
+ {-60.15, 1514.0},
+ {-50.15, 1513.0},
+ {-40.15, 1514.0},
+ {-30.15, 1513.0},
+ {-20.15, 1523.0},
+ {-19.65, 1566.0},
+ {-19.15, 1592.0},
+ {-18.65, 1927.0},
+ {-18.15, 3089.0},
+ {-17.65, 6068.0},
+ {-17.15, 14239.0},
+ {-16.65, 34124.0},
+ {-16.15, 64097.0},
+ {-15.65, 110352.0},
+ {-15.15, 164742.0},
+ {-14.65, 209499.0},
+ {-14.15, 267274.0},
+ {-13.65, 283290.0},
+ {-13.15, 275363.0},
+ {-12.65, 258014.0},
+ {-12.15, 214073.0},
+ {-11.65, 182244.0},
+ {-11.15, 136419.0},
+ {-10.65, 97823.0},
+ {-10.15, 58930.0},
+ { -9.65, 35404.0},
+ { -9.15, 16120.0},
+ { -8.65, 9823.0},
+ { -8.15, 5064.0},
+ { -7.65, 2575.0},
+ { -7.15, 1642.0},
+ { -6.65, 1101.0},
+ { -6.15, 812.0},
+ { -5.65, 690.0},
+ { -5.15, 565.0},
+ { 5.15, 564.0},
+ { 15.15, 565.0},
+ { 25.15, 564.0},
+ { 35.15, 565.0},
+ { 45.15, 564.0},
+ { 55.15, 565.0},
+ { 65.15, 564.0},
+ { 75.15, 565.0}
+ };
+ /** Poor data: right of peak is missing. */
+ protected static final double[][] DATASET4 = new double[][] {
+ {-20.15, 1523.0},
+ {-19.65, 1566.0},
+ {-19.15, 1592.0},
+ {-18.65, 1927.0},
+ {-18.15, 3089.0},
+ {-17.65, 6068.0},
+ {-17.15, 14239.0},
+ {-16.65, 34124.0},
+ {-16.15, 64097.0},
+ {-15.65, 110352.0},
+ {-15.15, 164742.0},
+ {-14.65, 209499.0},
+ {-14.15, 267274.0},
+ {-13.65, 283290.0}
+ };
+ /** Good data, but few points. */
+ protected static final double[][] DATASET5 = new double[][] {
+ {4.0254623, 531026.0},
+ {4.03128248, 984167.0},
+ {4.03839603, 1887233.0},
+ {4.04421621, 2687152.0},
+ {4.05132976, 3461228.0},
+ {4.05326982, 3580526.0},
+ {4.05779662, 3439750.0},
+ {4.0636168, 2877648.0},
+ {4.06943698, 2175960.0},
+ {4.07525716, 1447024.0},
+ {4.08237071, 717104.0},
+ {4.08366408, 620014.0}
+ };
+
+ /**
+ * Basic.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit01()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(DATASET1, fitter);
+ GaussianFunction fitFunction = fitter.fit();
+ assertEquals(99200.86969833552, fitFunction.getA(), 1e-4);
+ assertEquals(3410515.285208688, fitFunction.getB(), 1e-4);
+ assertEquals(4.054928275302832, fitFunction.getC(), 1e-4);
+ assertEquals(0.014609868872574, fitFunction.getD(), 1e-4);
+ }
+
+ /**
+ * Zero points is not enough observed points.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test(expected=IllegalArgumentException.class)
+ public void testFit02()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ fitter.fit();
+ }
+
+ /**
+ * Two points is not enough observed points.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test(expected=IllegalArgumentException.class)
+ public void testFit03()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(new double[][] {
+ {4.0254623, 531026.0},
+ {4.02804905, 664002.0}},
+ fitter);
+ fitter.fit();
+ }
+
+ /**
+ * Poor data: right of peak not symmetric with left of peak.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit04()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(DATASET2, fitter);
+ GaussianFunction fitFunction = fitter.fit();
+ assertEquals(-256534.689445631, fitFunction.getA(), 1e-4);
+ assertEquals(481328.2181530679, fitFunction.getB(), 1e-4);
+ assertEquals(-10.5217226891099, fitFunction.getC(), 1e-4);
+ assertEquals(-7.64248239366800, fitFunction.getD(), 1e-4);
+ }
+
+ /**
+ * Poor data: long tails.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit05()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(DATASET3, fitter);
+ GaussianFunction fitFunction = fitter.fit();
+ assertEquals(491.6310079258938, fitFunction.getA(), 1e-4);
+ assertEquals(283508.6800413632, fitFunction.getB(), 1e-4);
+ assertEquals(-13.2966857238057, fitFunction.getC(), 1e-4);
+ assertEquals(1.725590356962981, fitFunction.getD(), 1e-4);
+ }
+
+ /**
+ * Poor data: right of peak is missing.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit06()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(DATASET4, fitter);
+ GaussianFunction fitFunction = fitter.fit();
+ assertEquals(530.3649792355617, fitFunction.getA(), 1e-4);
+ assertEquals(284517.0835567514, fitFunction.getB(), 1e-4);
+ assertEquals(-13.5355534565105, fitFunction.getC(), 1e-4);
+ assertEquals(1.512353018625465, fitFunction.getD(), 1e-4);
+ }
+
+ /**
+ * Basic with smaller dataset.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit07()
+ throws OptimizationException, FunctionEvaluationException {
+ GaussianFitter fitter = new GaussianFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToGaussianFitter(DATASET5, fitter);
+ GaussianFunction fitFunction = fitter.fit();
+ assertEquals(176748.1400947575, fitFunction.getA(), 1e-4);
+ assertEquals(3361537.018813906, fitFunction.getB(), 1e-4);
+ assertEquals(4.054949992747176, fitFunction.getC(), 1e-4);
+ assertEquals(0.014192380137002, fitFunction.getD(), 1e-4);
+ }
+
+ /**
+ * Adds the specified points to specified <code>GaussianFitter</code>
+ * instance.
+ *
+ * @param points data points where first dimension is a point index and
+ * second dimension is an array of length two representing the point
+ * with the first value corresponding to X and the second value
+ * corresponding to Y
+ * @param fitter fitter to which the points in <code>points</code> should be
+ * added as observed points
+ */
+ protected static void addDatasetToGaussianFitter(double[][] points,
+ GaussianFitter fitter) {
+ for (int i = 0; i < points.length; i++) {
+ fitter.addObservedPoint(points[i][0], points[i][1]);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/fitting/HarmonicFitterTest.java b/src/test/java/org/apache/commons/math/optimization/fitting/HarmonicFitterTest.java
new file mode 100644
index 0000000..1aa4637
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/fitting/HarmonicFitterTest.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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Random;
+
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.MathUtils;
+import org.junit.Test;
+
+public class HarmonicFitterTest {
+
+ @Test
+ public void testNoError() throws OptimizationException {
+ HarmonicFunction f = new HarmonicFunction(0.2, 3.4, 4.1);
+
+ HarmonicFitter fitter =
+ new HarmonicFitter(new LevenbergMarquardtOptimizer());
+ for (double x = 0.0; x < 1.3; x += 0.01) {
+ fitter.addObservedPoint(1.0, x, f.value(x));
+ }
+
+ HarmonicFunction fitted = fitter.fit();
+ assertEquals(f.getAmplitude(), fitted.getAmplitude(), 1.0e-13);
+ assertEquals(f.getPulsation(), fitted.getPulsation(), 1.0e-13);
+ assertEquals(f.getPhase(), MathUtils.normalizeAngle(fitted.getPhase(), f.getPhase()), 1.0e-13);
+
+ for (double x = -1.0; x < 1.0; x += 0.01) {
+ assertTrue(FastMath.abs(f.value(x) - fitted.value(x)) < 1.0e-13);
+ }
+
+ }
+
+ @Test
+ public void test1PercentError() throws OptimizationException {
+ Random randomizer = new Random(64925784252l);
+ HarmonicFunction f = new HarmonicFunction(0.2, 3.4, 4.1);
+
+ HarmonicFitter fitter =
+ new HarmonicFitter(new LevenbergMarquardtOptimizer());
+ for (double x = 0.0; x < 10.0; x += 0.1) {
+ fitter.addObservedPoint(1.0, x,
+ f.value(x) + 0.01 * randomizer.nextGaussian());
+ }
+
+ HarmonicFunction fitted = fitter.fit();
+ assertEquals(f.getAmplitude(), fitted.getAmplitude(), 7.6e-4);
+ assertEquals(f.getPulsation(), fitted.getPulsation(), 2.7e-3);
+ assertEquals(f.getPhase(), MathUtils.normalizeAngle(fitted.getPhase(), f.getPhase()), 1.3e-2);
+
+ }
+
+ @Test
+ public void testInitialGuess() throws OptimizationException {
+ Random randomizer = new Random(45314242l);
+ HarmonicFunction f = new HarmonicFunction(0.2, 3.4, 4.1);
+
+ HarmonicFitter fitter =
+ new HarmonicFitter(new LevenbergMarquardtOptimizer(), new double[] { 0.15, 3.6, 4.5 });
+ for (double x = 0.0; x < 10.0; x += 0.1) {
+ fitter.addObservedPoint(1.0, x,
+ f.value(x) + 0.01 * randomizer.nextGaussian());
+ }
+
+ HarmonicFunction fitted = fitter.fit();
+ assertEquals(f.getAmplitude(), fitted.getAmplitude(), 1.2e-3);
+ assertEquals(f.getPulsation(), fitted.getPulsation(), 3.3e-3);
+ assertEquals(f.getPhase(), MathUtils.normalizeAngle(fitted.getPhase(), f.getPhase()), 1.7e-2);
+
+ }
+
+ @Test
+ public void testUnsorted() throws OptimizationException {
+ Random randomizer = new Random(64925784252l);
+ HarmonicFunction f = new HarmonicFunction(0.2, 3.4, 4.1);
+
+ HarmonicFitter fitter =
+ new HarmonicFitter(new LevenbergMarquardtOptimizer());
+
+ // build a regularly spaced array of measurements
+ int size = 100;
+ double[] xTab = new double[size];
+ double[] yTab = new double[size];
+ for (int i = 0; i < size; ++i) {
+ xTab[i] = 0.1 * i;
+ yTab[i] = f.value(xTab[i]) + 0.01 * randomizer.nextGaussian();
+ }
+
+ // shake it
+ for (int i = 0; i < size; ++i) {
+ int i1 = randomizer.nextInt(size);
+ int i2 = randomizer.nextInt(size);
+ double xTmp = xTab[i1];
+ double yTmp = yTab[i1];
+ xTab[i1] = xTab[i2];
+ yTab[i1] = yTab[i2];
+ xTab[i2] = xTmp;
+ yTab[i2] = yTmp;
+ }
+
+ // pass it to the fitter
+ for (int i = 0; i < size; ++i) {
+ fitter.addObservedPoint(1.0, xTab[i], yTab[i]);
+ }
+
+ HarmonicFunction fitted = fitter.fit();
+ assertEquals(f.getAmplitude(), fitted.getAmplitude(), 7.6e-4);
+ assertEquals(f.getPulsation(), fitted.getPulsation(), 3.5e-3);
+ assertEquals(f.getPhase(), MathUtils.normalizeAngle(fitted.getPhase(), f.getPhase()), 1.5e-2);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunctionTest.java b/src/test/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunctionTest.java
new file mode 100644
index 0000000..868a893
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/fitting/ParametricGaussianFunctionTest.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.optimization.fitting;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.exception.ZeroException;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+import org.apache.commons.math.optimization.general.
+ LevenbergMarquardtOptimizer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link ParametricGaussianFunction}.
+ *
+ * @since 2.2
+ * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
+ */
+public class ParametricGaussianFunctionTest {
+ /** Dataset 1 used by some test cases. */
+ protected static final double[][] DATASET1 = new double[][] {
+ {4.0254623, 531026.0},
+ {4.02804905, 664002.0},
+ {4.02934242, 787079.0},
+ {4.03128248, 984167.0},
+ {4.03386923, 1294546.0},
+ {4.03580929, 1560230.0},
+ {4.03839603, 1887233.0},
+ {4.0396894, 2113240.0},
+ {4.04162946, 2375211.0},
+ {4.04421621, 2687152.0},
+ {4.04550958, 2862644.0},
+ {4.04744964, 3078898.0},
+ {4.05003639, 3327238.0},
+ {4.05132976, 3461228.0},
+ {4.05326982, 3580526.0},
+ {4.05585657, 3576946.0},
+ {4.05779662, 3439750.0},
+ {4.06038337, 3220296.0},
+ {4.06167674, 3070073.0},
+ {4.0636168, 2877648.0},
+ {4.06620355, 2595848.0},
+ {4.06749692, 2390157.0},
+ {4.06943698, 2175960.0},
+ {4.07202373, 1895104.0},
+ {4.0733171, 1687576.0},
+ {4.07525716, 1447024.0},
+ {4.0778439, 1130879.0},
+ {4.07978396, 904900.0},
+ {4.08237071, 717104.0},
+ {4.08366408, 620014.0}
+ };
+
+ /**
+ * Using not-so-good initial parameters.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit01()
+ throws OptimizationException, FunctionEvaluationException {
+ CurveFitter fitter = new CurveFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToCurveFitter(DATASET1, fitter);
+ double[] parameters = fitter.fit(new ParametricGaussianFunction(),
+ new double[] {8.64753e3, 3.483323e6, 4.06322, 1.946857e-2});
+ assertEquals(99200.94715858076, parameters[0], 1e-4);
+ assertEquals(3410515.221897707, parameters[1], 1e-4);
+ assertEquals(4.054928275257894, parameters[2], 1e-4);
+ assertEquals(0.014609868499860, parameters[3], 1e-4);
+ }
+
+ /**
+ * Using eye-balled guesses for initial parameters.
+ *
+ * @throws OptimizationException in the event of a test case error
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test
+ public void testFit02()
+ throws OptimizationException, FunctionEvaluationException {
+ CurveFitter fitter = new CurveFitter(new LevenbergMarquardtOptimizer());
+ addDatasetToCurveFitter(DATASET1, fitter);
+ double[] parameters = fitter.fit(new ParametricGaussianFunction(),
+ new double[] {500000.0, 3500000.0, 4.055, 0.025479654});
+ assertEquals(99200.81836264656, parameters[0], 1e-4);
+ assertEquals(3410515.327151986, parameters[1], 1e-4);
+ assertEquals(4.054928275377392, parameters[2], 1e-4);
+ assertEquals(0.014609869119806, parameters[3], 1e-4);
+ }
+
+ /**
+ * The parameters array is null.
+ *
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test(expected=IllegalArgumentException.class)
+ public void testValue01() throws FunctionEvaluationException {
+ ParametricGaussianFunction f = new ParametricGaussianFunction();
+ f.value(0.0, null);
+ }
+
+ /**
+ * The parameters array length is not 4.
+ *
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test(expected=IllegalArgumentException.class)
+ public void testValue02() throws FunctionEvaluationException {
+ ParametricGaussianFunction f = new ParametricGaussianFunction();
+ f.value(0.0, new double[] {0.0, 1.0});
+ }
+
+ /**
+ * The parameters d is 0.
+ *
+ * @throws FunctionEvaluationException in the event of a test case error
+ */
+ @Test(expected=ZeroException.class)
+ public void testValue03() throws FunctionEvaluationException {
+ ParametricGaussianFunction f = new ParametricGaussianFunction();
+ f.value(0.0, new double[] {0.0, 1.0, 1.0, 0.0});
+ }
+
+ /**
+ * Adds the specified points to specified <code>CurveFitter</code> instance.
+ *
+ * @param points data points where first dimension is a point index and
+ * second dimension is an array of length two representing the point
+ * with the first value corresponding to X and the second value
+ * corresponding to Y
+ * @param fitter fitter to which the points in <code>points</code> should be
+ * added as observed points
+ */
+ protected static void addDatasetToCurveFitter(double[][] points,
+ CurveFitter fitter) {
+ for (int i = 0; i < points.length; i++) {
+ fitter.addObservedPoint(points[i][0], points[i][1]);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/fitting/PolynomialFitterTest.java b/src/test/java/org/apache/commons/math/optimization/fitting/PolynomialFitterTest.java
new file mode 100644
index 0000000..fe85640
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/fitting/PolynomialFitterTest.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.fitting;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Random;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
+import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class PolynomialFitterTest {
+
+ @Test
+ public void testNoError() throws OptimizationException {
+ Random randomizer = new Random(64925784252l);
+ for (int degree = 1; degree < 10; ++degree) {
+ PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
+
+ PolynomialFitter fitter =
+ new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
+ for (int i = 0; i <= degree; ++i) {
+ fitter.addObservedPoint(1.0, i, p.value(i));
+ }
+
+ PolynomialFunction fitted = fitter.fit();
+
+ for (double x = -1.0; x < 1.0; x += 0.01) {
+ double error = FastMath.abs(p.value(x) - fitted.value(x)) /
+ (1.0 + FastMath.abs(p.value(x)));
+ assertEquals(0.0, error, 1.0e-6);
+ }
+
+ }
+
+ }
+
+ @Test
+ public void testSmallError() throws OptimizationException {
+ Random randomizer = new Random(53882150042l);
+ double maxError = 0;
+ for (int degree = 0; degree < 10; ++degree) {
+ PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
+
+ PolynomialFitter fitter =
+ new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
+ for (double x = -1.0; x < 1.0; x += 0.01) {
+ fitter.addObservedPoint(1.0, x,
+ p.value(x) + 0.1 * randomizer.nextGaussian());
+ }
+
+ PolynomialFunction fitted = fitter.fit();
+
+ for (double x = -1.0; x < 1.0; x += 0.01) {
+ double error = FastMath.abs(p.value(x) - fitted.value(x)) /
+ (1.0 + FastMath.abs(p.value(x)));
+ maxError = FastMath.max(maxError, error);
+ assertTrue(FastMath.abs(error) < 0.1);
+ }
+ }
+ assertTrue(maxError > 0.01);
+
+ }
+
+ @Test
+ public void testRedundantSolvable() {
+ // Levenberg-Marquardt should handle redundant information gracefully
+ checkUnsolvableProblem(new LevenbergMarquardtOptimizer(), true);
+ }
+
+ @Test
+ public void testRedundantUnsolvable() {
+ // Gauss-Newton should not be able to solve redundant information
+ DifferentiableMultivariateVectorialOptimizer optimizer =
+ new GaussNewtonOptimizer(true);
+ checkUnsolvableProblem(optimizer, false);
+ }
+
+ private void checkUnsolvableProblem(DifferentiableMultivariateVectorialOptimizer optimizer,
+ boolean solvable) {
+ Random randomizer = new Random(1248788532l);
+ for (int degree = 0; degree < 10; ++degree) {
+ PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
+
+ PolynomialFitter fitter = new PolynomialFitter(degree, optimizer);
+
+ // reusing the same point over and over again does not bring
+ // information, the problem cannot be solved in this case for
+ // degrees greater than 1 (but one point is sufficient for
+ // degree 0)
+ for (double x = -1.0; x < 1.0; x += 0.01) {
+ fitter.addObservedPoint(1.0, 0.0, p.value(0.0));
+ }
+
+ try {
+ fitter.fit();
+ assertTrue(solvable || (degree == 0));
+ } catch(OptimizationException e) {
+ assertTrue((! solvable) && (degree > 0));
+ }
+
+ }
+
+ }
+
+ private PolynomialFunction buildRandomPolynomial(int degree, Random randomizer) {
+ final double[] coefficients = new double[degree + 1];
+ for (int i = 0; i <= degree; ++i) {
+ coefficients[i] = randomizer.nextGaussian();
+ }
+ return new PolynomialFunction(coefficients);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizerTest.java
new file mode 100644
index 0000000..0a326ac
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/general/GaussNewtonOptimizerTest.java
@@ -0,0 +1,572 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.awt.geom.Point2D;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+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.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.OptimizationException;
+import org.apache.commons.math.optimization.SimpleVectorialPointChecker;
+import org.apache.commons.math.optimization.SimpleVectorialValueChecker;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+public class GaussNewtonOptimizerTest
+extends TestCase {
+
+ public GaussNewtonOptimizerTest(String name) {
+ super(name);
+ }
+
+ public void testTrivial() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 2 } }, new double[] { 3 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1 }, new double[] { 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(1.5, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getValue()[0], 1.0e-10);
+ }
+
+ public void testColumnsPermutation() throws FunctionEvaluationException, OptimizationException {
+
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 1.0, -1.0 }, { 0.0, 2.0 }, { 1.0, -2.0 } },
+ new double[] { 4.0, 6.0, 1.0 });
+
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(7.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(4.0, optimum.getValue()[0], 1.0e-10);
+ assertEquals(6.0, optimum.getValue()[1], 1.0e-10);
+ assertEquals(1.0, optimum.getValue()[2], 1.0e-10);
+
+ }
+
+ public void testNoDependency() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 0, 0, 0, 0, 0 },
+ { 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 0 },
+ { 0, 0, 0, 0, 2, 0 },
+ { 0, 0, 0, 0, 0, 2 }
+ }, new double[] { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1, 1 },
+ new double[] { 0, 0, 0, 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ for (int i = 0; i < problem.target.length; ++i) {
+ assertEquals(0.55 * i, optimum.getPoint()[i], 1.0e-10);
+ }
+ }
+
+ public void testOneSet() throws FunctionEvaluationException, OptimizationException {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 0, 0 },
+ { -1, 1, 0 },
+ { 0, -1, 1 }
+ }, new double[] { 1, 1, 1});
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(1.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(2.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[2], 1.0e-10);
+
+ }
+
+ public void testTwoSets() throws FunctionEvaluationException, OptimizationException {
+ double epsilon = 1.0e-7;
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 1, 0, 4, 0, 0 },
+ { -4, -2, 3, -7, 0, 0 },
+ { 4, 1, -2, 8, 0, 0 },
+ { 0, -3, -12, -1, 0, 0 },
+ { 0, 0, 0, 0, epsilon, 1 },
+ { 0, 0, 0, 0, 1, 1 }
+ }, new double[] { 2, -9, 2, 2, 1 + epsilon * epsilon, 2});
+
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1, 1 },
+ new double[] { 0, 0, 0, 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals( 3.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals( 4.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(-1.0, optimum.getPoint()[2], 1.0e-10);
+ assertEquals(-2.0, optimum.getPoint()[3], 1.0e-10);
+ assertEquals( 1.0 + epsilon, optimum.getPoint()[4], 1.0e-10);
+ assertEquals( 1.0 - epsilon, optimum.getPoint()[5], 1.0e-10);
+
+ }
+
+ public void testNonInversible() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 2, -3 },
+ { 2, 1, 3 },
+ { -3, 0, -9 }
+ }, new double[] { 1, 1, 1 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ try {
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0, 0 });
+ fail("an exception should have been caught");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+ }
+
+ public void testIllConditioned() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem1 = new LinearProblem(new double[][] {
+ { 10.0, 7.0, 8.0, 7.0 },
+ { 7.0, 5.0, 6.0, 5.0 },
+ { 8.0, 6.0, 10.0, 9.0 },
+ { 7.0, 5.0, 9.0, 10.0 }
+ }, new double[] { 32, 23, 33, 31 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum1 =
+ optimizer.optimize(problem1, problem1.target, new double[] { 1, 1, 1, 1 },
+ new double[] { 0, 1, 2, 3 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[0], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[1], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[2], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[3], 1.0e-10);
+
+ LinearProblem problem2 = new LinearProblem(new double[][] {
+ { 10.00, 7.00, 8.10, 7.20 },
+ { 7.08, 5.04, 6.00, 5.00 },
+ { 8.00, 5.98, 9.89, 9.00 },
+ { 6.99, 4.99, 9.00, 9.98 }
+ }, new double[] { 32, 23, 33, 31 });
+ VectorialPointValuePair optimum2 =
+ optimizer.optimize(problem2, problem2.target, new double[] { 1, 1, 1, 1 },
+ new double[] { 0, 1, 2, 3 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(-81.0, optimum2.getPoint()[0], 1.0e-8);
+ assertEquals(137.0, optimum2.getPoint()[1], 1.0e-8);
+ assertEquals(-34.0, optimum2.getPoint()[2], 1.0e-8);
+ assertEquals( 22.0, optimum2.getPoint()[3], 1.0e-8);
+
+ }
+
+ public void testMoreEstimatedParametersSimple() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 3.0, 2.0, 0.0, 0.0 },
+ { 0.0, 1.0, -1.0, 1.0 },
+ { 2.0, 0.0, 1.0, 0.0 }
+ }, new double[] { 7.0, 3.0, 5.0 });
+
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ try {
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 },
+ new double[] { 7, 6, 5, 4 });
+ fail("an exception should have been caught");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ public void testMoreEstimatedParametersUnsorted() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 1.0, 1.0, 1.0, 0.0 },
+ { 0.0, 0.0, 0.0, 0.0, 1.0, -1.0 },
+ { 0.0, 0.0, -1.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 0.0, -1.0, 1.0, 0.0 }
+ }, new double[] { 3.0, 12.0, -1.0, 7.0, 1.0 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ try {
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 2, 2, 2, 2, 2, 2 });
+ fail("an exception should have been caught");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+ }
+
+ public void testRedundantEquations() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 5.0 });
+
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 },
+ new double[] { 1, 1 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(2.0, optimum.getPoint()[0], 1.0e-8);
+ assertEquals(1.0, optimum.getPoint()[1], 1.0e-8);
+
+ }
+
+ public void testInconsistentEquations() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 4.0 });
+
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 1, 1 });
+ assertTrue(optimizer.getRMS() > 0.1);
+
+ }
+
+ public void testInconsistentSizes() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 1, 0 }, { 0, 1 } }, new double[] { -1, 1 });
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1 }, new double[] { 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(-1, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(+1, optimum.getPoint()[1], 1.0e-10);
+
+ try {
+ optimizer.optimize(problem, problem.target,
+ new double[] { 1 },
+ new double[] { 0, 0 });
+ fail("an exception should have been thrown");
+ } catch (OptimizationException oe) {
+ // expected behavior
+ }
+
+ try {
+ optimizer.optimize(problem, new double[] { 1 },
+ new double[] { 1 },
+ new double[] { 0, 0 });
+ fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException oe) {
+ // expected behavior
+ }
+
+ }
+
+ public void testMaxEvaluations() throws Exception {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialPointChecker(1.0e-30, 1.0e-30));
+ try {
+ optimizer.optimize(circle, new double[] { 0, 0, 0, 0, 0 },
+ new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 98.680, 47.345 });
+ fail("an exception should have been caught");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+ }
+
+ public void testCircleFitting() throws FunctionEvaluationException, OptimizationException {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-13, 1.0e-13));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(circle, new double[] { 0, 0, 0, 0, 0 },
+ new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 98.680, 47.345 });
+ assertEquals(1.768262623567235, FastMath.sqrt(circle.getN()) * optimizer.getRMS(), 1.0e-10);
+ Point2D.Double center = new Point2D.Double(optimum.getPointRef()[0], optimum.getPointRef()[1]);
+ assertEquals(69.96016175359975, circle.getRadius(center), 1.0e-10);
+ assertEquals(96.07590209601095, center.x, 1.0e-10);
+ assertEquals(48.135167894714, center.y, 1.0e-10);
+ }
+
+ public void testCircleFittingBadInit() throws FunctionEvaluationException, OptimizationException {
+ Circle circle = new Circle();
+ double[][] points = new double[][] {
+ {-0.312967, 0.072366}, {-0.339248, 0.132965}, {-0.379780, 0.202724},
+ {-0.390426, 0.260487}, {-0.361212, 0.328325}, {-0.346039, 0.392619},
+ {-0.280579, 0.444306}, {-0.216035, 0.470009}, {-0.149127, 0.493832},
+ {-0.075133, 0.483271}, {-0.007759, 0.452680}, { 0.060071, 0.410235},
+ { 0.103037, 0.341076}, { 0.118438, 0.273884}, { 0.131293, 0.192201},
+ { 0.115869, 0.129797}, { 0.072223, 0.058396}, { 0.022884, 0.000718},
+ {-0.053355, -0.020405}, {-0.123584, -0.032451}, {-0.216248, -0.032862},
+ {-0.278592, -0.005008}, {-0.337655, 0.056658}, {-0.385899, 0.112526},
+ {-0.405517, 0.186957}, {-0.415374, 0.262071}, {-0.387482, 0.343398},
+ {-0.347322, 0.397943}, {-0.287623, 0.458425}, {-0.223502, 0.475513},
+ {-0.135352, 0.478186}, {-0.061221, 0.483371}, { 0.003711, 0.422737},
+ { 0.065054, 0.375830}, { 0.108108, 0.297099}, { 0.123882, 0.222850},
+ { 0.117729, 0.134382}, { 0.085195, 0.056820}, { 0.029800, -0.019138},
+ {-0.027520, -0.072374}, {-0.102268, -0.091555}, {-0.200299, -0.106578},
+ {-0.292731, -0.091473}, {-0.356288, -0.051108}, {-0.420561, 0.014926},
+ {-0.471036, 0.074716}, {-0.488638, 0.182508}, {-0.485990, 0.254068},
+ {-0.463943, 0.338438}, {-0.406453, 0.404704}, {-0.334287, 0.466119},
+ {-0.254244, 0.503188}, {-0.161548, 0.495769}, {-0.075733, 0.495560},
+ { 0.001375, 0.434937}, { 0.082787, 0.385806}, { 0.115490, 0.323807},
+ { 0.141089, 0.223450}, { 0.138693, 0.131703}, { 0.126415, 0.049174},
+ { 0.066518, -0.010217}, {-0.005184, -0.070647}, {-0.080985, -0.103635},
+ {-0.177377, -0.116887}, {-0.260628, -0.100258}, {-0.335756, -0.056251},
+ {-0.405195, -0.000895}, {-0.444937, 0.085456}, {-0.484357, 0.175597},
+ {-0.472453, 0.248681}, {-0.438580, 0.347463}, {-0.402304, 0.422428},
+ {-0.326777, 0.479438}, {-0.247797, 0.505581}, {-0.152676, 0.519380},
+ {-0.071754, 0.516264}, { 0.015942, 0.472802}, { 0.076608, 0.419077},
+ { 0.127673, 0.330264}, { 0.159951, 0.262150}, { 0.153530, 0.172681},
+ { 0.140653, 0.089229}, { 0.078666, 0.024981}, { 0.023807, -0.037022},
+ {-0.048837, -0.077056}, {-0.127729, -0.075338}, {-0.221271, -0.067526}
+ };
+ double[] target = new double[points.length];
+ Arrays.fill(target, 0.0);
+ double[] weights = new double[points.length];
+ Arrays.fill(weights, 2.0);
+ for (int i = 0; i < points.length; ++i) {
+ circle.addPoint(points[i][0], points[i][1]);
+ }
+ GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(true);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-6, 1.0e-6));
+ try {
+ optimizer.optimize(circle, target, weights, new double[] { -12, -12 });
+ fail("an exception should have been caught");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+
+ VectorialPointValuePair optimum =
+ optimizer.optimize(circle, target, weights, new double[] { 0, 0 });
+ assertEquals(-0.1517383071957963, optimum.getPointRef()[0], 1.0e-6);
+ assertEquals(0.2074999736353867, optimum.getPointRef()[1], 1.0e-6);
+ assertEquals(0.04268731682389561, optimizer.getRMS(), 1.0e-8);
+
+ }
+
+ private static class LinearProblem implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = -8804268799379350190L;
+ final RealMatrix factors;
+ final double[] target;
+ public LinearProblem(double[][] factors, double[] target) {
+ this.factors = new BlockRealMatrix(factors);
+ this.target = target;
+ }
+
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -8387467946663627585L;
+ public double[][] value(double[] point) {
+ return factors.getData();
+ }
+ };
+ }
+
+ }
+
+ private static class Circle implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = -7165774454925027042L;
+ private ArrayList<Point2D.Double> points;
+
+ public Circle() {
+ points = new ArrayList<Point2D.Double>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new Point2D.Double(px, py));
+ }
+
+ public int getN() {
+ return points.size();
+ }
+
+ public double getRadius(Point2D.Double center) {
+ double r = 0;
+ for (Point2D.Double point : points) {
+ r += point.distance(center);
+ }
+ return r / points.size();
+ }
+
+ private double[][] jacobian(double[] variables) {
+
+ int n = points.size();
+ Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
+
+ // gradient of the optimal radius
+ double dRdX = 0;
+ double dRdY = 0;
+ for (Point2D.Double pk : points) {
+ double dk = pk.distance(center);
+ dRdX += (center.x - pk.x) / dk;
+ dRdY += (center.y - pk.y) / dk;
+ }
+ dRdX /= n;
+ dRdY /= n;
+
+ // jacobian of the radius residuals
+ double[][] jacobian = new double[n][2];
+ for (int i = 0; i < n; ++i) {
+ Point2D.Double pi = points.get(i);
+ double di = pi.distance(center);
+ jacobian[i][0] = (center.x - pi.x) / di - dRdX;
+ jacobian[i][1] = (center.y - pi.y) / di - dRdY;
+ }
+
+ return jacobian;
+
+ }
+
+ public double[] value(double[] variables) {
+
+ Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
+ double radius = getRadius(center);
+
+ double[] residuals = new double[points.size()];
+ for (int i = 0; i < residuals.length; ++i) {
+ residuals[i] = points.get(i).distance(center) - radius;
+ }
+
+ return residuals;
+
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -4340046230875165095L;
+ public double[][] value(double[] point) {
+ return jacobian(point);
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java
new file mode 100644
index 0000000..7c50432
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.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.optimization.general;
+
+import java.awt.geom.Point2D;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+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.linear.BlockRealMatrix;
+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.VectorialPointValuePair;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+public class LevenbergMarquardtOptimizerTest
+ extends TestCase {
+
+ public LevenbergMarquardtOptimizerTest(String name) {
+ super(name);
+ }
+
+ public void testTrivial() throws Exception {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 2 } }, new double[] { 3 });
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1 }, new double[] { 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ try {
+ optimizer.guessParametersErrors();
+ fail("an exception should have been thrown");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+ assertEquals(1.5, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getValue()[0], 1.0e-10);
+ }
+
+ public void testQRColumnsPermutation() throws Exception {
+
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 1.0, -1.0 }, { 0.0, 2.0 }, { 1.0, -2.0 } },
+ new double[] { 4.0, 6.0, 1.0 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(7.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(4.0, optimum.getValue()[0], 1.0e-10);
+ assertEquals(6.0, optimum.getValue()[1], 1.0e-10);
+ assertEquals(1.0, optimum.getValue()[2], 1.0e-10);
+
+ }
+
+ public void testNoDependency() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 0, 0, 0, 0, 0 },
+ { 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 0 },
+ { 0, 0, 0, 0, 2, 0 },
+ { 0, 0, 0, 0, 0, 2 }
+ }, new double[] { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 });
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1, 1 },
+ new double[] { 0, 0, 0, 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ for (int i = 0; i < problem.target.length; ++i) {
+ assertEquals(0.55 * i, optimum.getPoint()[i], 1.0e-10);
+ }
+ }
+
+ public void testOneSet() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 0, 0 },
+ { -1, 1, 0 },
+ { 0, -1, 1 }
+ }, new double[] { 1, 1, 1});
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(1.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(2.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[2], 1.0e-10);
+
+ }
+
+ public void testTwoSets() throws Exception {
+ double epsilon = 1.0e-7;
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 1, 0, 4, 0, 0 },
+ { -4, -2, 3, -7, 0, 0 },
+ { 4, 1, -2, 8, 0, 0 },
+ { 0, -3, -12, -1, 0, 0 },
+ { 0, 0, 0, 0, epsilon, 1 },
+ { 0, 0, 0, 0, 1, 1 }
+ }, new double[] { 2, -9, 2, 2, 1 + epsilon * epsilon, 2});
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1, 1 },
+ new double[] { 0, 0, 0, 0, 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals( 3.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals( 4.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(-1.0, optimum.getPoint()[2], 1.0e-10);
+ assertEquals(-2.0, optimum.getPoint()[3], 1.0e-10);
+ assertEquals( 1.0 + epsilon, optimum.getPoint()[4], 1.0e-10);
+ assertEquals( 1.0 - epsilon, optimum.getPoint()[5], 1.0e-10);
+
+ }
+
+ public void testNonInversible() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 2, -3 },
+ { 2, 1, 3 },
+ { -3, 0, -9 }
+ }, new double[] { 1, 1, 1 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 0, 0, 0 });
+ assertTrue(FastMath.sqrt(problem.target.length) * optimizer.getRMS() > 0.6);
+ try {
+ optimizer.getCovariances();
+ fail("an exception should have been thrown");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ public void testIllConditioned() throws Exception {
+ LinearProblem problem1 = new LinearProblem(new double[][] {
+ { 10.0, 7.0, 8.0, 7.0 },
+ { 7.0, 5.0, 6.0, 5.0 },
+ { 8.0, 6.0, 10.0, 9.0 },
+ { 7.0, 5.0, 9.0, 10.0 }
+ }, new double[] { 32, 23, 33, 31 });
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum1 =
+ optimizer.optimize(problem1, problem1.target, new double[] { 1, 1, 1, 1 },
+ new double[] { 0, 1, 2, 3 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[0], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[1], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[2], 1.0e-10);
+ assertEquals(1.0, optimum1.getPoint()[3], 1.0e-10);
+
+ LinearProblem problem2 = new LinearProblem(new double[][] {
+ { 10.00, 7.00, 8.10, 7.20 },
+ { 7.08, 5.04, 6.00, 5.00 },
+ { 8.00, 5.98, 9.89, 9.00 },
+ { 6.99, 4.99, 9.00, 9.98 }
+ }, new double[] { 32, 23, 33, 31 });
+ VectorialPointValuePair optimum2 =
+ optimizer.optimize(problem2, problem2.target, new double[] { 1, 1, 1, 1 },
+ new double[] { 0, 1, 2, 3 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(-81.0, optimum2.getPoint()[0], 1.0e-8);
+ assertEquals(137.0, optimum2.getPoint()[1], 1.0e-8);
+ assertEquals(-34.0, optimum2.getPoint()[2], 1.0e-8);
+ assertEquals( 22.0, optimum2.getPoint()[3], 1.0e-8);
+
+ }
+
+ public void testMoreEstimatedParametersSimple() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 3.0, 2.0, 0.0, 0.0 },
+ { 0.0, 1.0, -1.0, 1.0 },
+ { 2.0, 0.0, 1.0, 0.0 }
+ }, new double[] { 7.0, 3.0, 5.0 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 },
+ new double[] { 7, 6, 5, 4 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+
+ }
+
+ public void testMoreEstimatedParametersUnsorted() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 1.0, 1.0, 1.0, 0.0 },
+ { 0.0, 0.0, 0.0, 0.0, 1.0, -1.0 },
+ { 0.0, 0.0, -1.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 0.0, -1.0, 1.0, 0.0 }
+ }, new double[] { 3.0, 12.0, -1.0, 7.0, 1.0 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 2, 2, 2, 2, 2, 2 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(3.0, optimum.getPointRef()[2], 1.0e-10);
+ assertEquals(4.0, optimum.getPointRef()[3], 1.0e-10);
+ assertEquals(5.0, optimum.getPointRef()[4], 1.0e-10);
+ assertEquals(6.0, optimum.getPointRef()[5], 1.0e-10);
+
+ }
+
+ public void testRedundantEquations() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 5.0 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 },
+ new double[] { 1, 1 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(2.0, optimum.getPointRef()[0], 1.0e-10);
+ assertEquals(1.0, optimum.getPointRef()[1], 1.0e-10);
+
+ }
+
+ public void testInconsistentEquations() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 4.0 });
+
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1, 1 }, new double[] { 1, 1 });
+ assertTrue(optimizer.getRMS() > 0.1);
+
+ }
+
+ public void testInconsistentSizes() throws FunctionEvaluationException, OptimizationException {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 1, 0 }, { 0, 1 } }, new double[] { -1, 1 });
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+
+ VectorialPointValuePair optimum =
+ optimizer.optimize(problem, problem.target, new double[] { 1, 1 }, new double[] { 0, 0 });
+ assertEquals(0, optimizer.getRMS(), 1.0e-10);
+ assertEquals(-1, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(+1, optimum.getPoint()[1], 1.0e-10);
+
+ try {
+ optimizer.optimize(problem, problem.target,
+ new double[] { 1 },
+ new double[] { 0, 0 });
+ fail("an exception should have been thrown");
+ } catch (OptimizationException oe) {
+ // expected behavior
+ }
+
+ try {
+ optimizer.optimize(problem, new double[] { 1 },
+ new double[] { 1 },
+ new double[] { 0, 0 });
+ fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException oe) {
+ // expected behavior
+ }
+
+ }
+
+ public void testControlParameters() {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ checkEstimate(circle, 0.1, 10, 1.0e-14, 1.0e-16, 1.0e-10, false);
+ checkEstimate(circle, 0.1, 10, 1.0e-15, 1.0e-17, 1.0e-10, true);
+ checkEstimate(circle, 0.1, 5, 1.0e-15, 1.0e-16, 1.0e-10, true);
+ circle.addPoint(300, -300);
+ checkEstimate(circle, 0.1, 20, 1.0e-18, 1.0e-16, 1.0e-10, true);
+ }
+
+ private void checkEstimate(DifferentiableMultivariateVectorialFunction problem,
+ double initialStepBoundFactor, int maxCostEval,
+ double costRelativeTolerance, double parRelativeTolerance,
+ double orthoTolerance, boolean shouldFail) {
+ try {
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.setInitialStepBoundFactor(initialStepBoundFactor);
+ optimizer.setMaxIterations(maxCostEval);
+ optimizer.setCostRelativeTolerance(costRelativeTolerance);
+ optimizer.setParRelativeTolerance(parRelativeTolerance);
+ optimizer.setOrthoTolerance(orthoTolerance);
+ optimizer.optimize(problem, new double[] { 0, 0, 0, 0, 0 }, new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 98.680, 47.345 });
+ assertTrue(! shouldFail);
+ } catch (OptimizationException ee) {
+ assertTrue(shouldFail);
+ } catch (FunctionEvaluationException ee) {
+ assertTrue(shouldFail);
+ }
+ }
+
+ public void testCircleFitting() throws Exception {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ VectorialPointValuePair optimum =
+ optimizer.optimize(circle, new double[] { 0, 0, 0, 0, 0 }, new double[] { 1, 1, 1, 1, 1 },
+ new double[] { 98.680, 47.345 });
+ assertTrue(optimizer.getEvaluations() < 10);
+ assertTrue(optimizer.getJacobianEvaluations() < 10);
+ double rms = optimizer.getRMS();
+ assertEquals(1.768262623567235, FastMath.sqrt(circle.getN()) * rms, 1.0e-10);
+ Point2D.Double center = new Point2D.Double(optimum.getPointRef()[0], optimum.getPointRef()[1]);
+ assertEquals(69.96016176931406, circle.getRadius(center), 1.0e-10);
+ assertEquals(96.07590211815305, center.x, 1.0e-10);
+ assertEquals(48.13516790438953, center.y, 1.0e-10);
+ double[][] cov = optimizer.getCovariances();
+ assertEquals(1.839, cov[0][0], 0.001);
+ assertEquals(0.731, cov[0][1], 0.001);
+ assertEquals(cov[0][1], cov[1][0], 1.0e-14);
+ assertEquals(0.786, cov[1][1], 0.001);
+ double[] errors = optimizer.guessParametersErrors();
+ assertEquals(1.384, errors[0], 0.001);
+ assertEquals(0.905, errors[1], 0.001);
+
+ // add perfect measurements and check errors are reduced
+ double r = circle.getRadius(center);
+ for (double d= 0; d < 2 * FastMath.PI; d += 0.01) {
+ circle.addPoint(center.x + r * FastMath.cos(d), center.y + r * FastMath.sin(d));
+ }
+ double[] target = new double[circle.getN()];
+ Arrays.fill(target, 0.0);
+ double[] weights = new double[circle.getN()];
+ Arrays.fill(weights, 2.0);
+ optimizer.optimize(circle, target, weights, new double[] { 98.680, 47.345 });
+ cov = optimizer.getCovariances();
+ assertEquals(0.0016, cov[0][0], 0.001);
+ assertEquals(3.2e-7, cov[0][1], 1.0e-9);
+ assertEquals(cov[0][1], cov[1][0], 1.0e-14);
+ assertEquals(0.0016, cov[1][1], 0.001);
+ errors = optimizer.guessParametersErrors();
+ assertEquals(0.004, errors[0], 0.001);
+ assertEquals(0.004, errors[1], 0.001);
+
+ }
+
+ public void testCircleFittingBadInit() throws FunctionEvaluationException, OptimizationException {
+ Circle circle = new Circle();
+ double[][] points = new double[][] {
+ {-0.312967, 0.072366}, {-0.339248, 0.132965}, {-0.379780, 0.202724},
+ {-0.390426, 0.260487}, {-0.361212, 0.328325}, {-0.346039, 0.392619},
+ {-0.280579, 0.444306}, {-0.216035, 0.470009}, {-0.149127, 0.493832},
+ {-0.075133, 0.483271}, {-0.007759, 0.452680}, { 0.060071, 0.410235},
+ { 0.103037, 0.341076}, { 0.118438, 0.273884}, { 0.131293, 0.192201},
+ { 0.115869, 0.129797}, { 0.072223, 0.058396}, { 0.022884, 0.000718},
+ {-0.053355, -0.020405}, {-0.123584, -0.032451}, {-0.216248, -0.032862},
+ {-0.278592, -0.005008}, {-0.337655, 0.056658}, {-0.385899, 0.112526},
+ {-0.405517, 0.186957}, {-0.415374, 0.262071}, {-0.387482, 0.343398},
+ {-0.347322, 0.397943}, {-0.287623, 0.458425}, {-0.223502, 0.475513},
+ {-0.135352, 0.478186}, {-0.061221, 0.483371}, { 0.003711, 0.422737},
+ { 0.065054, 0.375830}, { 0.108108, 0.297099}, { 0.123882, 0.222850},
+ { 0.117729, 0.134382}, { 0.085195, 0.056820}, { 0.029800, -0.019138},
+ {-0.027520, -0.072374}, {-0.102268, -0.091555}, {-0.200299, -0.106578},
+ {-0.292731, -0.091473}, {-0.356288, -0.051108}, {-0.420561, 0.014926},
+ {-0.471036, 0.074716}, {-0.488638, 0.182508}, {-0.485990, 0.254068},
+ {-0.463943, 0.338438}, {-0.406453, 0.404704}, {-0.334287, 0.466119},
+ {-0.254244, 0.503188}, {-0.161548, 0.495769}, {-0.075733, 0.495560},
+ { 0.001375, 0.434937}, { 0.082787, 0.385806}, { 0.115490, 0.323807},
+ { 0.141089, 0.223450}, { 0.138693, 0.131703}, { 0.126415, 0.049174},
+ { 0.066518, -0.010217}, {-0.005184, -0.070647}, {-0.080985, -0.103635},
+ {-0.177377, -0.116887}, {-0.260628, -0.100258}, {-0.335756, -0.056251},
+ {-0.405195, -0.000895}, {-0.444937, 0.085456}, {-0.484357, 0.175597},
+ {-0.472453, 0.248681}, {-0.438580, 0.347463}, {-0.402304, 0.422428},
+ {-0.326777, 0.479438}, {-0.247797, 0.505581}, {-0.152676, 0.519380},
+ {-0.071754, 0.516264}, { 0.015942, 0.472802}, { 0.076608, 0.419077},
+ { 0.127673, 0.330264}, { 0.159951, 0.262150}, { 0.153530, 0.172681},
+ { 0.140653, 0.089229}, { 0.078666, 0.024981}, { 0.023807, -0.037022},
+ {-0.048837, -0.077056}, {-0.127729, -0.075338}, {-0.221271, -0.067526}
+ };
+ double[] target = new double[points.length];
+ Arrays.fill(target, 0.0);
+ double[] weights = new double[points.length];
+ Arrays.fill(weights, 2.0);
+ for (int i = 0; i < points.length; ++i) {
+ circle.addPoint(points[i][0], points[i][1]);
+ }
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.setConvergenceChecker(new SimpleVectorialValueChecker(1.0e-8, 1.0e-8));
+ VectorialPointValuePair optimum =
+ optimizer.optimize(circle, target, weights, new double[] { -12, -12 });
+ Point2D.Double center = new Point2D.Double(optimum.getPointRef()[0], optimum.getPointRef()[1]);
+ assertTrue(optimizer.getEvaluations() < 25);
+ assertTrue(optimizer.getJacobianEvaluations() < 20);
+ assertEquals( 0.043, optimizer.getRMS(), 1.0e-3);
+ assertEquals( 0.292235, circle.getRadius(center), 1.0e-6);
+ assertEquals(-0.151738, center.x, 1.0e-6);
+ assertEquals( 0.2075001, center.y, 1.0e-6);
+ }
+
+ public void testMath199() throws FunctionEvaluationException {
+ try {
+ QuadraticProblem problem = new QuadraticProblem();
+ problem.addPoint (0, -3.182591015485607);
+ problem.addPoint (1, -2.5581184967730577);
+ problem.addPoint (2, -2.1488478161387325);
+ problem.addPoint (3, -1.9122489313410047);
+ problem.addPoint (4, 1.7785661310051026);
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.setQRRankingThreshold(0);
+ optimizer.optimize(problem,
+ new double[] { 0, 0, 0, 0, 0 },
+ new double[] { 0.0, 4.4e-323, 1.0, 4.4e-323, 0.0 },
+ new double[] { 0, 0, 0 });
+ fail("an exception should have been thrown");
+ } catch (OptimizationException ee) {
+ // expected behavior
+ }
+
+ }
+
+ private static class LinearProblem implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = 703247177355019415L;
+ final RealMatrix factors;
+ final double[] target;
+ public LinearProblem(double[][] factors, double[] target) {
+ this.factors = new BlockRealMatrix(factors);
+ this.target = target;
+ }
+
+ public double[] value(double[] variables) {
+ return factors.operate(variables);
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = 556396458721526234L;
+ public double[][] value(double[] point) {
+ return factors.getData();
+ }
+ };
+ }
+
+ }
+
+ private static class Circle implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = -4711170319243817874L;
+
+ private ArrayList<Point2D.Double> points;
+
+ public Circle() {
+ points = new ArrayList<Point2D.Double>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new Point2D.Double(px, py));
+ }
+
+ public int getN() {
+ return points.size();
+ }
+
+ public double getRadius(Point2D.Double center) {
+ double r = 0;
+ for (Point2D.Double point : points) {
+ r += point.distance(center);
+ }
+ return r / points.size();
+ }
+
+ private double[][] jacobian(double[] point) {
+
+ int n = points.size();
+ Point2D.Double center = new Point2D.Double(point[0], point[1]);
+
+ // gradient of the optimal radius
+ double dRdX = 0;
+ double dRdY = 0;
+ for (Point2D.Double pk : points) {
+ double dk = pk.distance(center);
+ dRdX += (center.x - pk.x) / dk;
+ dRdY += (center.y - pk.y) / dk;
+ }
+ dRdX /= n;
+ dRdY /= n;
+
+ // jacobian of the radius residuals
+ double[][] jacobian = new double[n][2];
+ for (int i = 0; i < n; ++i) {
+ Point2D.Double pi = points.get(i);
+ double di = pi.distance(center);
+ jacobian[i][0] = (center.x - pi.x) / di - dRdX;
+ jacobian[i][1] = (center.y - pi.y) / di - dRdY;
+ }
+
+ return jacobian;
+
+ }
+
+ public double[] value(double[] variables)
+ throws FunctionEvaluationException, IllegalArgumentException {
+
+ Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
+ double radius = getRadius(center);
+
+ double[] residuals = new double[points.size()];
+ for (int i = 0; i < residuals.length; ++i) {
+ residuals[i] = points.get(i).distance(center) - radius;
+ }
+
+ return residuals;
+
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -4340046230875165095L;
+ public double[][] value(double[] point) {
+ return jacobian(point);
+ }
+ };
+ }
+
+ }
+
+ private static class QuadraticProblem implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = 7072187082052755854L;
+ private List<Double> x;
+ private List<Double> y;
+
+ public QuadraticProblem() {
+ x = new ArrayList<Double>();
+ y = new ArrayList<Double>();
+ }
+
+ public void addPoint(double x, double y) {
+ this.x.add(x);
+ this.y.add(y);
+ }
+
+ private double[][] jacobian(double[] variables) {
+ double[][] jacobian = new double[x.size()][3];
+ for (int i = 0; i < jacobian.length; ++i) {
+ jacobian[i][0] = x.get(i) * x.get(i);
+ jacobian[i][1] = x.get(i);
+ jacobian[i][2] = 1.0;
+ }
+ return jacobian;
+ }
+
+ public double[] value(double[] variables) {
+ double[] values = new double[x.size()];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = (variables[0] * x.get(i) + variables[1]) * x.get(i) + variables[2];
+ }
+ return values;
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -8673650298627399464L;
+ public double[][] value(double[] point) {
+ return jacobian(point);
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/general/MinpackTest.java b/src/test/java/org/apache/commons/math/optimization/general/MinpackTest.java
new file mode 100644
index 0000000..fe3d5bf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/general/MinpackTest.java
@@ -0,0 +1,1531 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.io.Serializable;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+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.OptimizationException;
+import org.apache.commons.math.optimization.VectorialPointValuePair;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+public class MinpackTest extends TestCase {
+
+ public MinpackTest(String name) {
+ super(name);
+ }
+
+ public void testMinpackLinearFullRank() {
+ minpackTest(new LinearFullRankFunction(10, 5, 1.0,
+ 5.0, 2.23606797749979), false);
+ minpackTest(new LinearFullRankFunction(50, 5, 1.0,
+ 8.06225774829855, 6.70820393249937), false);
+ }
+
+ public void testMinpackLinearRank1() {
+ minpackTest(new LinearRank1Function(10, 5, 1.0,
+ 291.521868819476, 1.4638501094228), false);
+ minpackTest(new LinearRank1Function(50, 5, 1.0,
+ 3101.60039334535, 3.48263016573496), false);
+ }
+
+ public void testMinpackLinearRank1ZeroColsAndRows() {
+ minpackTest(new LinearRank1ZeroColsAndRowsFunction(10, 5, 1.0), false);
+ minpackTest(new LinearRank1ZeroColsAndRowsFunction(50, 5, 1.0), false);
+ }
+
+ public void testMinpackRosenbrok() {
+ minpackTest(new RosenbrockFunction(new double[] { -1.2, 1.0 },
+ FastMath.sqrt(24.2)), false);
+ minpackTest(new RosenbrockFunction(new double[] { -12.0, 10.0 },
+ FastMath.sqrt(1795769.0)), false);
+ minpackTest(new RosenbrockFunction(new double[] { -120.0, 100.0 },
+ 11.0 * FastMath.sqrt(169000121.0)), false);
+ }
+
+ public void testMinpackHelicalValley() {
+ minpackTest(new HelicalValleyFunction(new double[] { -1.0, 0.0, 0.0 },
+ 50.0), false);
+ minpackTest(new HelicalValleyFunction(new double[] { -10.0, 0.0, 0.0 },
+ 102.95630140987), false);
+ minpackTest(new HelicalValleyFunction(new double[] { -100.0, 0.0, 0.0},
+ 991.261822123701), false);
+ }
+
+ public void testMinpackPowellSingular() {
+ minpackTest(new PowellSingularFunction(new double[] { 3.0, -1.0, 0.0, 1.0 },
+ 14.6628782986152), false);
+ minpackTest(new PowellSingularFunction(new double[] { 30.0, -10.0, 0.0, 10.0 },
+ 1270.9838708654), false);
+ minpackTest(new PowellSingularFunction(new double[] { 300.0, -100.0, 0.0, 100.0 },
+ 126887.903284750), false);
+ }
+
+ public void testMinpackFreudensteinRoth() {
+ minpackTest(new FreudensteinRothFunction(new double[] { 0.5, -2.0 },
+ 20.0124960961895, 6.99887517584575,
+ new double[] {
+ 11.4124844654993,
+ -0.896827913731509
+ }), false);
+ minpackTest(new FreudensteinRothFunction(new double[] { 5.0, -20.0 },
+ 12432.833948863, 6.9988751744895,
+ new double[] {
+ 11.41300466147456,
+ -0.896796038685959
+ }), false);
+ minpackTest(new FreudensteinRothFunction(new double[] { 50.0, -200.0 },
+ 11426454.595762, 6.99887517242903,
+ new double[] {
+ 11.412781785788564,
+ -0.8968051074920405
+ }), false);
+ }
+
+ public void testMinpackBard() {
+ minpackTest(new BardFunction(1.0, 6.45613629515967, 0.0906359603390466,
+ new double[] {
+ 0.0824105765758334,
+ 1.1330366534715,
+ 2.34369463894115
+ }), false);
+ minpackTest(new BardFunction(10.0, 36.1418531596785, 4.17476870138539,
+ new double[] {
+ 0.840666673818329,
+ -158848033.259565,
+ -164378671.653535
+ }), false);
+ minpackTest(new BardFunction(100.0, 384.114678637399, 4.17476870135969,
+ new double[] {
+ 0.840666673867645,
+ -158946167.205518,
+ -164464906.857771
+ }), false);
+ }
+
+ public void testMinpackKowalikOsborne() {
+ minpackTest(new KowalikOsborneFunction(new double[] { 0.25, 0.39, 0.415, 0.39 },
+ 0.0728915102882945,
+ 0.017535837721129,
+ new double[] {
+ 0.192807810476249,
+ 0.191262653354071,
+ 0.123052801046931,
+ 0.136053221150517
+ }), false);
+ minpackTest(new KowalikOsborneFunction(new double[] { 2.5, 3.9, 4.15, 3.9 },
+ 2.97937007555202,
+ 0.032052192917937,
+ new double[] {
+ 728675.473768287,
+ -14.0758803129393,
+ -32977797.7841797,
+ -20571594.1977912
+ }), false);
+ minpackTest(new KowalikOsborneFunction(new double[] { 25.0, 39.0, 41.5, 39.0 },
+ 29.9590617016037,
+ 0.0175364017658228,
+ new double[] {
+ 0.192948328597594,
+ 0.188053165007911,
+ 0.122430604321144,
+ 0.134575665392506
+ }), false);
+ }
+
+ public void testMinpackMeyer() {
+ minpackTest(new MeyerFunction(new double[] { 0.02, 4000.0, 250.0 },
+ 41153.4665543031, 9.37794514651874,
+ new double[] {
+ 0.00560963647102661,
+ 6181.34634628659,
+ 345.223634624144
+ }), false);
+ minpackTest(new MeyerFunction(new double[] { 0.2, 40000.0, 2500.0 },
+ 4168216.89130846, 792.917871779501,
+ new double[] {
+ 1.42367074157994e-11,
+ 33695.7133432541,
+ 901.268527953801
+ }), true);
+ }
+
+ public void testMinpackWatson() {
+
+ minpackTest(new WatsonFunction(6, 0.0,
+ 5.47722557505166, 0.0478295939097601,
+ new double[] {
+ -0.0157249615083782, 1.01243488232965,
+ -0.232991722387673, 1.26043101102818,
+ -1.51373031394421, 0.99299727291842
+ }), false);
+ minpackTest(new WatsonFunction(6, 10.0,
+ 6433.12578950026, 0.0478295939096951,
+ new double[] {
+ -0.0157251901386677, 1.01243485860105,
+ -0.232991545843829, 1.26042932089163,
+ -1.51372776706575, 0.99299573426328
+ }), false);
+ minpackTest(new WatsonFunction(6, 100.0,
+ 674256.040605213, 0.047829593911544,
+ new double[] {
+ -0.0157247019712586, 1.01243490925658,
+ -0.232991922761641, 1.26043292929555,
+ -1.51373320452707, 0.99299901922322
+ }), false);
+
+ minpackTest(new WatsonFunction(9, 0.0,
+ 5.47722557505166, 0.00118311459212420,
+ new double[] {
+ -0.153070644166722e-4, 0.999789703934597,
+ 0.0147639634910978, 0.146342330145992,
+ 1.00082109454817, -2.61773112070507,
+ 4.10440313943354, -3.14361226236241,
+ 1.05262640378759
+ }), false);
+ minpackTest(new WatsonFunction(9, 10.0,
+ 12088.127069307, 0.00118311459212513,
+ new double[] {
+ -0.153071334849279e-4, 0.999789703941234,
+ 0.0147639629786217, 0.146342334818836,
+ 1.00082107321386, -2.61773107084722,
+ 4.10440307655564, -3.14361222178686,
+ 1.05262639322589
+ }), false);
+ minpackTest(new WatsonFunction(9, 100.0,
+ 1269109.29043834, 0.00118311459212384,
+ new double[] {
+ -0.153069523352176e-4, 0.999789703958371,
+ 0.0147639625185392, 0.146342341096326,
+ 1.00082104729164, -2.61773101573645,
+ 4.10440301427286, -3.14361218602503,
+ 1.05262638516774
+ }), false);
+
+ minpackTest(new WatsonFunction(12, 0.0,
+ 5.47722557505166, 0.217310402535861e-4,
+ new double[] {
+ -0.660266001396382e-8, 1.00000164411833,
+ -0.000563932146980154, 0.347820540050756,
+ -0.156731500244233, 1.05281515825593,
+ -3.24727109519451, 7.2884347837505,
+ -10.271848098614, 9.07411353715783,
+ -4.54137541918194, 1.01201187975044
+ }), false);
+ minpackTest(new WatsonFunction(12, 10.0,
+ 19220.7589790951, 0.217310402518509e-4,
+ new double[] {
+ -0.663710223017410e-8, 1.00000164411787,
+ -0.000563932208347327, 0.347820540486998,
+ -0.156731503955652, 1.05281517654573,
+ -3.2472711515214, 7.28843489430665,
+ -10.2718482369638, 9.07411364383733,
+ -4.54137546533666, 1.01201188830857
+ }), false);
+ minpackTest(new WatsonFunction(12, 100.0,
+ 2018918.04462367, 0.217310402539845e-4,
+ new double[] {
+ -0.663806046485249e-8, 1.00000164411786,
+ -0.000563932210324959, 0.347820540503588,
+ -0.156731504091375, 1.05281517718031,
+ -3.24727115337025, 7.28843489775302,
+ -10.2718482410813, 9.07411364688464,
+ -4.54137546660822, 1.0120118885369
+ }), false);
+
+ }
+
+ public void testMinpackBox3Dimensional() {
+ minpackTest(new Box3DimensionalFunction(10, new double[] { 0.0, 10.0, 20.0 },
+ 32.1115837449572), false);
+ }
+
+ public void testMinpackJennrichSampson() {
+ minpackTest(new JennrichSampsonFunction(10, new double[] { 0.3, 0.4 },
+ 64.5856498144943, 11.1517793413499,
+ new double[] {
+ // 0.2578330049, 0.257829976764542
+ 0.2578199266368004, 0.25782997676455244
+ }), false);
+ }
+
+ public void testMinpackBrownDennis() {
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 25.0, 5.0, -5.0, -1.0 },
+ 2815.43839161816, 292.954288244866,
+ new double[] {
+ -11.59125141003, 13.2024883984741,
+ -0.403574643314272, 0.236736269844604
+ }), false);
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 250.0, 50.0, -50.0, -10.0 },
+ 555073.354173069, 292.954270581415,
+ new double[] {
+ -11.5959274272203, 13.2041866926242,
+ -0.403417362841545, 0.236771143410386
+ }), false);
+ minpackTest(new BrownDennisFunction(20,
+ new double[] { 2500.0, 500.0, -500.0, -100.0 },
+ 61211252.2338581, 292.954306151134,
+ new double[] {
+ -11.5902596937374, 13.2020628854665,
+ -0.403688070279258, 0.236665033746463
+ }), false);
+ }
+
+ public void testMinpackChebyquad() {
+ minpackTest(new ChebyquadFunction(1, 8, 1.0,
+ 1.88623796907732, 1.88623796907732,
+ new double[] { 0.5 }), false);
+ minpackTest(new ChebyquadFunction(1, 8, 10.0,
+ 5383344372.34005, 1.88424820499951,
+ new double[] { 0.9817314924684 }), false);
+ minpackTest(new ChebyquadFunction(1, 8, 100.0,
+ 0.118088726698392e19, 1.88424820499347,
+ new double[] { 0.9817314852934 }), false);
+ minpackTest(new ChebyquadFunction(8, 8, 1.0,
+ 0.196513862833975, 0.0593032355046727,
+ new double[] {
+ 0.0431536648587336, 0.193091637843267,
+ 0.266328593812698, 0.499999334628884,
+ 0.500000665371116, 0.733671406187302,
+ 0.806908362156733, 0.956846335141266
+ }), false);
+ minpackTest(new ChebyquadFunction(9, 9, 1.0,
+ 0.16994993465202, 0.0,
+ new double[] {
+ 0.0442053461357828, 0.199490672309881,
+ 0.23561910847106, 0.416046907892598,
+ 0.5, 0.583953092107402,
+ 0.764380891528940, 0.800509327690119,
+ 0.955794653864217
+ }), false);
+ minpackTest(new ChebyquadFunction(10, 10, 1.0,
+ 0.183747831178711, 0.0806471004038253,
+ new double[] {
+ 0.0596202671753563, 0.166708783805937,
+ 0.239171018813509, 0.398885290346268,
+ 0.398883667870681, 0.601116332129320,
+ 0.60111470965373, 0.760828981186491,
+ 0.833291216194063, 0.940379732824644
+ }), false);
+ }
+
+ public void testMinpackBrownAlmostLinear() {
+ minpackTest(new BrownAlmostLinearFunction(10, 0.5,
+ 16.5302162063499, 0.0,
+ new double[] {
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 0.979430303349862,
+ 0.979430303349862, 1.20569696650138
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(10, 5.0,
+ 9765624.00089211, 0.0,
+ new double[] {
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 0.979430303349865,
+ 0.979430303349865, 1.20569696650135
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(10, 50.0,
+ 0.9765625e17, 0.0,
+ new double[] {
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(30, 0.5,
+ 83.476044467848, 0.0,
+ new double[] {
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 0.997754216442807,
+ 0.997754216442807, 1.06737350671578
+ }), false);
+ minpackTest(new BrownAlmostLinearFunction(40, 0.5,
+ 128.026364472323, 0.0,
+ new double[] {
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 1.00000000000002, 1.00000000000002,
+ 0.999999999999121
+ }), false);
+ }
+
+ public void testMinpackOsborne1() {
+ minpackTest(new Osborne1Function(new double[] { 0.5, 1.5, -1.0, 0.01, 0.02, },
+ 0.937564021037838, 0.00739249260904843,
+ new double[] {
+ 0.375410049244025, 1.93584654543108,
+ -1.46468676748716, 0.0128675339110439,
+ 0.0221227011813076
+ }), false);
+ }
+
+ public void testMinpackOsborne2() {
+
+ minpackTest(new Osborne2Function(new double[] {
+ 1.3, 0.65, 0.65, 0.7, 0.6,
+ 3.0, 5.0, 7.0, 2.0, 4.5, 5.5
+ },
+ 1.44686540984712, 0.20034404483314,
+ new double[] {
+ 1.30997663810096, 0.43155248076,
+ 0.633661261602859, 0.599428560991695,
+ 0.754179768272449, 0.904300082378518,
+ 1.36579949521007, 4.82373199748107,
+ 2.39868475104871, 4.56887554791452,
+ 5.67534206273052
+ }), false);
+ }
+
+ private void minpackTest(MinpackFunction function, boolean exceptionExpected) {
+ LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
+ optimizer.setMaxIterations(100 * (function.getN() + 1));
+ optimizer.setCostRelativeTolerance(FastMath.sqrt(2.22044604926e-16));
+ optimizer.setParRelativeTolerance(FastMath.sqrt(2.22044604926e-16));
+ optimizer.setOrthoTolerance(2.22044604926e-16);
+// assertTrue(function.checkTheoreticalStartCost(optimizer.getRMS()));
+ try {
+ VectorialPointValuePair optimum =
+ optimizer.optimize(function,
+ function.getTarget(), function.getWeight(),
+ function.getStartPoint());
+ assertFalse(exceptionExpected);
+ function.checkTheoreticalMinCost(optimizer.getRMS());
+ function.checkTheoreticalMinParams(optimum);
+ } catch (OptimizationException lsse) {
+ assertTrue(exceptionExpected);
+ } catch (FunctionEvaluationException fe) {
+ assertTrue(exceptionExpected);
+ }
+ }
+
+ private static abstract class MinpackFunction
+ implements DifferentiableMultivariateVectorialFunction, Serializable {
+
+ private static final long serialVersionUID = -6209760235478794233L;
+ protected int n;
+ protected int m;
+ protected double[] startParams;
+ protected double theoreticalMinCost;
+ protected double[] theoreticalMinParams;
+ protected double costAccuracy;
+ protected double paramsAccuracy;
+
+ protected MinpackFunction(int m, double[] startParams,
+ double theoreticalMinCost, double[] theoreticalMinParams) {
+ this.m = m;
+ this.n = startParams.length;
+ this.startParams = startParams.clone();
+ this.theoreticalMinCost = theoreticalMinCost;
+ this.theoreticalMinParams = theoreticalMinParams;
+ this.costAccuracy = 1.0e-8;
+ this.paramsAccuracy = 1.0e-5;
+ }
+
+ protected static double[] buildArray(int n, double x) {
+ double[] array = new double[n];
+ Arrays.fill(array, x);
+ return array;
+ }
+
+ public double[] getTarget() {
+ return buildArray(m, 0.0);
+ }
+
+ public double[] getWeight() {
+ return buildArray(m, 1.0);
+ }
+
+ public double[] getStartPoint() {
+ return startParams.clone();
+ }
+
+ protected void setCostAccuracy(double costAccuracy) {
+ this.costAccuracy = costAccuracy;
+ }
+
+ protected void setParamsAccuracy(double paramsAccuracy) {
+ this.paramsAccuracy = paramsAccuracy;
+ }
+
+ public int getN() {
+ return startParams.length;
+ }
+
+ public void checkTheoreticalMinCost(double rms) {
+ double threshold = costAccuracy * (1.0 + theoreticalMinCost);
+ assertEquals(theoreticalMinCost, FastMath.sqrt(m) * rms, threshold);
+ }
+
+ public void checkTheoreticalMinParams(VectorialPointValuePair optimum) {
+ double[] params = optimum.getPointRef();
+ if (theoreticalMinParams != null) {
+ for (int i = 0; i < theoreticalMinParams.length; ++i) {
+ double mi = theoreticalMinParams[i];
+ double vi = params[i];
+ assertEquals(mi, vi, paramsAccuracy * (1.0 + FastMath.abs(mi)));
+ }
+ }
+ }
+
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ private static final long serialVersionUID = -2435076097232923678L;
+ public double[][] value(double[] point) {
+ return jacobian(point);
+ }
+ };
+ }
+
+ public abstract double[][] jacobian(double[] variables);
+
+ public abstract double[] value(double[] variables);
+
+ }
+
+ private static class LinearFullRankFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -9030323226268039536L;
+
+ public LinearFullRankFunction(int m, int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost) {
+ super(m, buildArray(n, x0), theoreticalMinCost,
+ buildArray(n, -1.0));
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double t = 2.0 / m;
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = (i == j) ? (1 - t) : -t;
+ }
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += variables[i];
+ }
+ double t = 1 + 2 * sum / m;
+ double[] f = new double[m];
+ for (int i = 0; i < n; ++i) {
+ f[i] = variables[i] - t;
+ }
+ Arrays.fill(f, n, m, -t);
+ return f;
+ }
+
+ }
+
+ private static class LinearRank1Function extends MinpackFunction {
+
+ private static final long serialVersionUID = 8494863245104608300L;
+
+ public LinearRank1Function(int m, int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost) {
+ super(m, buildArray(n, x0), theoreticalMinCost, null);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = (i + 1) * (j + 1);
+ }
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double[] f = new double[m];
+ double sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += (i + 1) * variables[i];
+ }
+ for (int i = 0; i < m; ++i) {
+ f[i] = (i + 1) * sum - 1;
+ }
+ return f;
+ }
+
+ }
+
+ private static class LinearRank1ZeroColsAndRowsFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -3316653043091995018L;
+
+ public LinearRank1ZeroColsAndRowsFunction(int m, int n, double x0) {
+ super(m, buildArray(n, x0),
+ FastMath.sqrt((m * (m + 3) - 6) / (2.0 * (2 * m - 3))),
+ null);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ jacobian[i][0] = 0;
+ for (int j = 1; j < (n - 1); ++j) {
+ if (i == 0) {
+ jacobian[i][j] = 0;
+ } else if (i != (m - 1)) {
+ jacobian[i][j] = i * (j + 1);
+ } else {
+ jacobian[i][j] = 0;
+ }
+ }
+ jacobian[i][n - 1] = 0;
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double[] f = new double[m];
+ double sum = 0;
+ for (int i = 1; i < (n - 1); ++i) {
+ sum += (i + 1) * variables[i];
+ }
+ for (int i = 0; i < (m - 1); ++i) {
+ f[i] = i * sum - 1;
+ }
+ f[m - 1] = -1;
+ return f;
+ }
+
+ }
+
+ private static class RosenbrockFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 2893438180956569134L;
+
+ public RosenbrockFunction(double[] startParams, double theoreticalStartCost) {
+ super(2, startParams, 0.0, buildArray(2, 1.0));
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ return new double[][] { { -20 * x1, 10 }, { -1, 0 } };
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ return new double[] { 10 * (x2 - x1 * x1), 1 - x1 };
+ }
+
+ }
+
+ private static class HelicalValleyFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 220613787843200102L;
+
+ public HelicalValleyFunction(double[] startParams,
+ double theoreticalStartCost) {
+ super(3, startParams, 0.0, new double[] { 1.0, 0.0, 0.0 });
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double tmpSquare = x1 * x1 + x2 * x2;
+ double tmp1 = twoPi * tmpSquare;
+ double tmp2 = FastMath.sqrt(tmpSquare);
+ return new double[][] {
+ { 100 * x2 / tmp1, -100 * x1 / tmp1, 10 },
+ { 10 * x1 / tmp2, 10 * x2 / tmp2, 0 },
+ { 0, 0, 1 }
+ };
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double tmp1;
+ if (x1 == 0) {
+ tmp1 = (x2 >= 0) ? 0.25 : -0.25;
+ } else {
+ tmp1 = FastMath.atan(x2 / x1) / twoPi;
+ if (x1 < 0) {
+ tmp1 += 0.5;
+ }
+ }
+ double tmp2 = FastMath.sqrt(x1 * x1 + x2 * x2);
+ return new double[] {
+ 10.0 * (x3 - 10 * tmp1),
+ 10.0 * (tmp2 - 1),
+ x3
+ };
+ }
+
+ private static final double twoPi = 2.0 * FastMath.PI;
+
+ }
+
+ private static class PowellSingularFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 7298364171208142405L;
+
+ public PowellSingularFunction(double[] startParams,
+ double theoreticalStartCost) {
+ super(4, startParams, 0.0, buildArray(4, 0.0));
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ return new double[][] {
+ { 1, 10, 0, 0 },
+ { 0, 0, sqrt5, -sqrt5 },
+ { 0, 2 * (x2 - 2 * x3), -4 * (x2 - 2 * x3), 0 },
+ { 2 * sqrt10 * (x1 - x4), 0, 0, -2 * sqrt10 * (x1 - x4) }
+ };
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ return new double[] {
+ x1 + 10 * x2,
+ sqrt5 * (x3 - x4),
+ (x2 - 2 * x3) * (x2 - 2 * x3),
+ sqrt10 * (x1 - x4) * (x1 - x4)
+ };
+ }
+
+ private static final double sqrt5 = FastMath.sqrt( 5.0);
+ private static final double sqrt10 = FastMath.sqrt(10.0);
+
+ }
+
+ private static class FreudensteinRothFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 2892404999344244214L;
+
+ public FreudensteinRothFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(2, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x2 = variables[1];
+ return new double[][] {
+ { 1, x2 * (10 - 3 * x2) - 2 },
+ { 1, x2 * ( 2 + 3 * x2) - 14, }
+ };
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ return new double[] {
+ -13.0 + x1 + ((5.0 - x2) * x2 - 2.0) * x2,
+ -29.0 + x1 + ((1.0 + x2) * x2 - 14.0) * x2
+ };
+ }
+
+ }
+
+ private static class BardFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 5990442612572087668L;
+
+ public BardFunction(double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(15, buildArray(3, x0), theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp1 = i + 1;
+ double tmp2 = 15 - i;
+ double tmp3 = (i <= 7) ? tmp1 : tmp2;
+ double tmp4 = x2 * tmp2 + x3 * tmp3;
+ tmp4 *= tmp4;
+ jacobian[i] = new double[] { -1, tmp1 * tmp2 / tmp4, tmp1 * tmp3 / tmp4 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double tmp1 = i + 1;
+ double tmp2 = 15 - i;
+ double tmp3 = (i <= 7) ? tmp1 : tmp2;
+ f[i] = y[i] - (x1 + tmp1 / (x2 * tmp2 + x3 * tmp3));
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 0.14, 0.18, 0.22, 0.25, 0.29,
+ 0.32, 0.35, 0.39, 0.37, 0.58,
+ 0.73, 0.96, 1.34, 2.10, 4.39
+ };
+
+ }
+
+ private static class KowalikOsborneFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -4867445739880495801L;
+
+ public KowalikOsborneFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(11, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ if (theoreticalStartCost > 20.0) {
+ setCostAccuracy(2.0e-4);
+ setParamsAccuracy(5.0e-3);
+ }
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp = v[i] * (v[i] + x3) + x4;
+ double j1 = -v[i] * (v[i] + x2) / tmp;
+ double j2 = -v[i] * x1 / tmp;
+ double j3 = j1 * j2;
+ double j4 = j3 / v[i];
+ jacobian[i] = new double[] { j1, j2, j3, j4 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ f[i] = y[i] - x1 * (v[i] * (v[i] + x2)) / (v[i] * (v[i] + x3) + x4);
+ }
+ return f;
+ }
+
+ private static final double[] v = {
+ 4.0, 2.0, 1.0, 0.5, 0.25, 0.167, 0.125, 0.1, 0.0833, 0.0714, 0.0625
+ };
+
+ private static final double[] y = {
+ 0.1957, 0.1947, 0.1735, 0.1600, 0.0844, 0.0627,
+ 0.0456, 0.0342, 0.0323, 0.0235, 0.0246
+ };
+
+ }
+
+ private static class MeyerFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -838060619150131027L;
+
+ public MeyerFunction(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(16, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ if (theoreticalStartCost > 1.0e6) {
+ setCostAccuracy(7.0e-3);
+ setParamsAccuracy(2.0e-2);
+ }
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = 5.0 * (i + 1) + 45.0 + x3;
+ double tmp1 = x2 / temp;
+ double tmp2 = FastMath.exp(tmp1);
+ double tmp3 = x1 * tmp2 / temp;
+ jacobian[i] = new double[] { tmp2, tmp3, -tmp1 * tmp3 };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ f[i] = x1 * FastMath.exp(x2 / (5.0 * (i + 1) + 45.0 + x3)) - y[i];
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 34780.0, 28610.0, 23650.0, 19630.0,
+ 16370.0, 13720.0, 11540.0, 9744.0,
+ 8261.0, 7030.0, 6005.0, 5147.0,
+ 4427.0, 3820.0, 3307.0, 2872.0
+ };
+
+ }
+
+ private static class WatsonFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -9034759294980218927L;
+
+ public WatsonFunction(int n, double x0,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(31, buildArray(n, x0), theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+
+ double[][] jacobian = new double[m][];
+
+ for (int i = 0; i < (m - 2); ++i) {
+ double div = (i + 1) / 29.0;
+ double s2 = 0.0;
+ double dx = 1.0;
+ for (int j = 0; j < n; ++j) {
+ s2 += dx * variables[j];
+ dx *= div;
+ }
+ double temp= 2 * div * s2;
+ dx = 1.0 / div;
+ jacobian[i] = new double[n];
+ for (int j = 0; j < n; ++j) {
+ jacobian[i][j] = dx * (j - temp);
+ dx *= div;
+ }
+ }
+
+ jacobian[m - 2] = new double[n];
+ jacobian[m - 2][0] = 1;
+
+ jacobian[m - 1] = new double[n];
+ jacobian[m - 1][0]= -2 * variables[0];
+ jacobian[m - 1][1]= 1;
+
+ return jacobian;
+
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double[] f = new double[m];
+ for (int i = 0; i < (m - 2); ++i) {
+ double div = (i + 1) / 29.0;
+ double s1 = 0;
+ double dx = 1;
+ for (int j = 1; j < n; ++j) {
+ s1 += j * dx * variables[j];
+ dx *= div;
+ }
+ double s2 =0;
+ dx =1;
+ for (int j = 0; j < n; ++j) {
+ s2 += dx * variables[j];
+ dx *= div;
+ }
+ f[i] = s1 - s2 * s2 - 1;
+ }
+
+ double x1 = variables[0];
+ double x2 = variables[1];
+ f[m - 2] = x1;
+ f[m - 1] = x2 - x1 * x1 - 1;
+
+ return f;
+
+ }
+
+ }
+
+ private static class Box3DimensionalFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 5511403858142574493L;
+
+ public Box3DimensionalFunction(int m, double[] startParams,
+ double theoreticalStartCost) {
+ super(m, startParams, 0.0,
+ new double[] { 1.0, 10.0, 1.0 });
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double tmp = (i + 1) / 10.0;
+ jacobian[i] = new double[] {
+ -tmp * FastMath.exp(-tmp * x1),
+ tmp * FastMath.exp(-tmp * x2),
+ FastMath.exp(-i - 1) - FastMath.exp(-tmp)
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double tmp = (i + 1) / 10.0;
+ f[i] = FastMath.exp(-tmp * x1) - FastMath.exp(-tmp * x2)
+ + (FastMath.exp(-i - 1) - FastMath.exp(-tmp)) * x3;
+ }
+ return f;
+ }
+
+ }
+
+ private static class JennrichSampsonFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -2489165190443352947L;
+
+ public JennrichSampsonFunction(int m, double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double t = i + 1;
+ jacobian[i] = new double[] { -t * FastMath.exp(t * x1), -t * FastMath.exp(t * x2) };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = i + 1;
+ f[i] = 2 + 2 * temp - FastMath.exp(temp * x1) - FastMath.exp(temp * x2);
+ }
+ return f;
+ }
+
+ }
+
+ private static class BrownDennisFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 8340018645694243910L;
+
+ public BrownDennisFunction(int m, double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ setCostAccuracy(2.5e-8);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = (i + 1) / 5.0;
+ double ti = FastMath.sin(temp);
+ double tmp1 = x1 + temp * x2 - FastMath.exp(temp);
+ double tmp2 = x3 + ti * x4 - FastMath.cos(temp);
+ jacobian[i] = new double[] {
+ 2 * tmp1, 2 * temp * tmp1, 2 * tmp2, 2 * ti * tmp2
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = (i + 1) / 5.0;
+ double tmp1 = x1 + temp * x2 - FastMath.exp(temp);
+ double tmp2 = x3 + FastMath.sin(temp) * x4 - FastMath.cos(temp);
+ f[i] = tmp1 * tmp1 + tmp2 * tmp2;
+ }
+ return f;
+ }
+
+ }
+
+ private static class ChebyquadFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = -2394877275028008594L;
+
+ private static double[] buildChebyquadArray(int n, double factor) {
+ double[] array = new double[n];
+ double inv = factor / (n + 1);
+ for (int i = 0; i < n; ++i) {
+ array[i] = (i + 1) * inv;
+ }
+ return array;
+ }
+
+ public ChebyquadFunction(int n, int m, double factor,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, buildChebyquadArray(n, factor), theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ }
+
+ double dx = 1.0 / n;
+ for (int j = 0; j < n; ++j) {
+ double tmp1 = 1;
+ double tmp2 = 2 * variables[j] - 1;
+ double temp = 2 * tmp2;
+ double tmp3 = 0;
+ double tmp4 = 2;
+ for (int i = 0; i < m; ++i) {
+ jacobian[i][j] = dx * tmp4;
+ double ti = 4 * tmp2 + temp * tmp4 - tmp3;
+ tmp3 = tmp4;
+ tmp4 = ti;
+ ti = temp * tmp2 - tmp1;
+ tmp1 = tmp2;
+ tmp2 = ti;
+ }
+ }
+
+ return jacobian;
+
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+
+ double[] f = new double[m];
+
+ for (int j = 0; j < n; ++j) {
+ double tmp1 = 1;
+ double tmp2 = 2 * variables[j] - 1;
+ double temp = 2 * tmp2;
+ for (int i = 0; i < m; ++i) {
+ f[i] += tmp2;
+ double ti = temp * tmp2 - tmp1;
+ tmp1 = tmp2;
+ tmp2 = ti;
+ }
+ }
+
+ double dx = 1.0 / n;
+ boolean iev = false;
+ for (int i = 0; i < m; ++i) {
+ f[i] *= dx;
+ if (iev) {
+ f[i] += 1.0 / (i * (i + 2));
+ }
+ iev = ! iev;
+ }
+
+ return f;
+
+ }
+
+ }
+
+ private static class BrownAlmostLinearFunction extends MinpackFunction {
+
+ private static final long serialVersionUID = 8239594490466964725L;
+
+ public BrownAlmostLinearFunction(int m, double factor,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(m, buildArray(m, factor), theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ jacobian[i] = new double[n];
+ }
+
+ double prod = 1;
+ for (int j = 0; j < n; ++j) {
+ prod *= variables[j];
+ for (int i = 0; i < n; ++i) {
+ jacobian[i][j] = 1;
+ }
+ jacobian[j][j] = 2;
+ }
+
+ for (int j = 0; j < n; ++j) {
+ double temp = variables[j];
+ if (temp == 0) {
+ temp = 1;
+ prod = 1;
+ for (int k = 0; k < n; ++k) {
+ if (k != j) {
+ prod *= variables[k];
+ }
+ }
+ }
+ jacobian[n - 1][j] = prod / temp;
+ }
+
+ return jacobian;
+
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double[] f = new double[m];
+ double sum = -(n + 1);
+ double prod = 1;
+ for (int j = 0; j < n; ++j) {
+ sum += variables[j];
+ prod *= variables[j];
+ }
+ for (int i = 0; i < n; ++i) {
+ f[i] = variables[i] + sum;
+ }
+ f[n - 1] = prod - 1;
+ return f;
+ }
+
+ }
+
+ private static class Osborne1Function extends MinpackFunction {
+
+ private static final long serialVersionUID = 4006743521149849494L;
+
+ public Osborne1Function(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(33, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double x5 = variables[4];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = 10.0 * i;
+ double tmp1 = FastMath.exp(-temp * x4);
+ double tmp2 = FastMath.exp(-temp * x5);
+ jacobian[i] = new double[] {
+ -1, -tmp1, -tmp2, temp * x2 * tmp1, temp * x3 * tmp2
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x1 = variables[0];
+ double x2 = variables[1];
+ double x3 = variables[2];
+ double x4 = variables[3];
+ double x5 = variables[4];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = 10.0 * i;
+ double tmp1 = FastMath.exp(-temp * x4);
+ double tmp2 = FastMath.exp(-temp * x5);
+ f[i] = y[i] - (x1 + x2 * tmp1 + x3 * tmp2);
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 0.844, 0.908, 0.932, 0.936, 0.925, 0.908, 0.881, 0.850, 0.818, 0.784, 0.751,
+ 0.718, 0.685, 0.658, 0.628, 0.603, 0.580, 0.558, 0.538, 0.522, 0.506, 0.490,
+ 0.478, 0.467, 0.457, 0.448, 0.438, 0.431, 0.424, 0.420, 0.414, 0.411, 0.406
+ };
+
+ }
+
+ private static class Osborne2Function extends MinpackFunction {
+
+ private static final long serialVersionUID = -8418268780389858746L;
+
+ public Osborne2Function(double[] startParams,
+ double theoreticalStartCost,
+ double theoreticalMinCost,
+ double[] theoreticalMinParams) {
+ super(65, startParams, theoreticalMinCost,
+ theoreticalMinParams);
+ }
+
+ @Override
+ public double[][] jacobian(double[] variables) {
+ double x01 = variables[0];
+ double x02 = variables[1];
+ double x03 = variables[2];
+ double x04 = variables[3];
+ double x05 = variables[4];
+ double x06 = variables[5];
+ double x07 = variables[6];
+ double x08 = variables[7];
+ double x09 = variables[8];
+ double x10 = variables[9];
+ double x11 = variables[10];
+ double[][] jacobian = new double[m][];
+ for (int i = 0; i < m; ++i) {
+ double temp = i / 10.0;
+ double tmp1 = FastMath.exp(-x05 * temp);
+ double tmp2 = FastMath.exp(-x06 * (temp - x09) * (temp - x09));
+ double tmp3 = FastMath.exp(-x07 * (temp - x10) * (temp - x10));
+ double tmp4 = FastMath.exp(-x08 * (temp - x11) * (temp - x11));
+ jacobian[i] = new double[] {
+ -tmp1,
+ -tmp2,
+ -tmp3,
+ -tmp4,
+ temp * x01 * tmp1,
+ x02 * (temp - x09) * (temp - x09) * tmp2,
+ x03 * (temp - x10) * (temp - x10) * tmp3,
+ x04 * (temp - x11) * (temp - x11) * tmp4,
+ -2 * x02 * x06 * (temp - x09) * tmp2,
+ -2 * x03 * x07 * (temp - x10) * tmp3,
+ -2 * x04 * x08 * (temp - x11) * tmp4
+ };
+ }
+ return jacobian;
+ }
+
+ @Override
+ public double[] value(double[] variables) {
+ double x01 = variables[0];
+ double x02 = variables[1];
+ double x03 = variables[2];
+ double x04 = variables[3];
+ double x05 = variables[4];
+ double x06 = variables[5];
+ double x07 = variables[6];
+ double x08 = variables[7];
+ double x09 = variables[8];
+ double x10 = variables[9];
+ double x11 = variables[10];
+ double[] f = new double[m];
+ for (int i = 0; i < m; ++i) {
+ double temp = i / 10.0;
+ double tmp1 = FastMath.exp(-x05 * temp);
+ double tmp2 = FastMath.exp(-x06 * (temp - x09) * (temp - x09));
+ double tmp3 = FastMath.exp(-x07 * (temp - x10) * (temp - x10));
+ double tmp4 = FastMath.exp(-x08 * (temp - x11) * (temp - x11));
+ f[i] = y[i] - (x01 * tmp1 + x02 * tmp2 + x03 * tmp3 + x04 * tmp4);
+ }
+ return f;
+ }
+
+ private static final double[] y = {
+ 1.366, 1.191, 1.112, 1.013, 0.991,
+ 0.885, 0.831, 0.847, 0.786, 0.725,
+ 0.746, 0.679, 0.608, 0.655, 0.616,
+ 0.606, 0.602, 0.626, 0.651, 0.724,
+ 0.649, 0.649, 0.694, 0.644, 0.624,
+ 0.661, 0.612, 0.558, 0.533, 0.495,
+ 0.500, 0.423, 0.395, 0.375, 0.372,
+ 0.391, 0.396, 0.405, 0.428, 0.429,
+ 0.523, 0.562, 0.607, 0.653, 0.672,
+ 0.708, 0.633, 0.668, 0.645, 0.632,
+ 0.591, 0.559, 0.597, 0.625, 0.739,
+ 0.710, 0.729, 0.720, 0.636, 0.581,
+ 0.428, 0.292, 0.162, 0.098, 0.054
+ };
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizerTest.java
new file mode 100644
index 0000000..cd53293
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/general/NonLinearConjugateGradientOptimizerTest.java
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.optimization.general;
+
+import java.awt.geom.Point2D;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateRealFunction;
+import org.apache.commons.math.analysis.MultivariateVectorialFunction;
+import org.apache.commons.math.analysis.solvers.BrentSolver;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.RealPointValuePair;
+import org.apache.commons.math.optimization.SimpleScalarValueChecker;
+
+/**
+ * <p>Some of the unit tests are re-implementations of the MINPACK <a
+ * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
+ * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files.
+ * 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>
+
+ * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
+ * @author Burton S. Garbow (original fortran minpack tests)
+ * @author Kenneth E. Hillstrom (original fortran minpack tests)
+ * @author Jorge J. More (original fortran minpack tests)
+ * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
+ */
+public class NonLinearConjugateGradientOptimizerTest
+extends TestCase {
+
+ public NonLinearConjugateGradientOptimizerTest(String name) {
+ super(name);
+ }
+
+ public void testTrivial() throws Exception {
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 2 } }, new double[] { 3 });
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0 });
+ assertEquals(1.5, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(0.0, optimum.getValue(), 1.0e-10);
+ }
+
+ public void testColumnsPermutation() throws Exception {
+
+ LinearProblem problem =
+ new LinearProblem(new double[][] { { 1.0, -1.0 }, { 0.0, 2.0 }, { 1.0, -2.0 } },
+ new double[] { 4.0, 6.0, 1.0 });
+
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0 });
+ assertEquals(7.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(0.0, optimum.getValue(), 1.0e-10);
+
+ }
+
+ public void testNoDependency() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 0, 0, 0, 0, 0 },
+ { 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 0 },
+ { 0, 0, 0, 0, 2, 0 },
+ { 0, 0, 0, 0, 0, 2 }
+ }, new double[] { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 });
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0, 0, 0, 0 });
+ for (int i = 0; i < problem.target.length; ++i) {
+ assertEquals(0.55 * i, optimum.getPoint()[i], 1.0e-10);
+ }
+ }
+
+ public void testOneSet() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 0, 0 },
+ { -1, 1, 0 },
+ { 0, -1, 1 }
+ }, new double[] { 1, 1, 1});
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0 });
+ assertEquals(1.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals(2.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(3.0, optimum.getPoint()[2], 1.0e-10);
+
+ }
+
+ public void testTwoSets() throws Exception {
+ final double epsilon = 1.0e-7;
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 2, 1, 0, 4, 0, 0 },
+ { -4, -2, 3, -7, 0, 0 },
+ { 4, 1, -2, 8, 0, 0 },
+ { 0, -3, -12, -1, 0, 0 },
+ { 0, 0, 0, 0, epsilon, 1 },
+ { 0, 0, 0, 0, 1, 1 }
+ }, new double[] { 2, -9, 2, 2, 1 + epsilon * epsilon, 2});
+
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setPreconditioner(new Preconditioner() {
+ public double[] precondition(double[] point, double[] r) {
+ double[] d = r.clone();
+ d[0] /= 72.0;
+ d[1] /= 30.0;
+ d[2] /= 314.0;
+ d[3] /= 260.0;
+ d[4] /= 2 * (1 + epsilon * epsilon);
+ d[5] /= 4.0;
+ return d;
+ }
+ });
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-13, 1.0e-13));
+
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0, 0, 0, 0 });
+ assertEquals( 3.0, optimum.getPoint()[0], 1.0e-10);
+ assertEquals( 4.0, optimum.getPoint()[1], 1.0e-10);
+ assertEquals(-1.0, optimum.getPoint()[2], 1.0e-10);
+ assertEquals(-2.0, optimum.getPoint()[3], 1.0e-10);
+ assertEquals( 1.0 + epsilon, optimum.getPoint()[4], 1.0e-10);
+ assertEquals( 1.0 - epsilon, optimum.getPoint()[5], 1.0e-10);
+
+ }
+
+ public void testNonInversible() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1, 2, -3 },
+ { 2, 1, 3 },
+ { -3, 0, -9 }
+ }, new double[] { 1, 1, 1 });
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0 });
+ assertTrue(optimum.getValue() > 0.5);
+ }
+
+ public void testIllConditioned() throws Exception {
+ LinearProblem problem1 = new LinearProblem(new double[][] {
+ { 10.0, 7.0, 8.0, 7.0 },
+ { 7.0, 5.0, 6.0, 5.0 },
+ { 8.0, 6.0, 10.0, 9.0 },
+ { 7.0, 5.0, 9.0, 10.0 }
+ }, new double[] { 32, 23, 33, 31 });
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-13, 1.0e-13));
+ BrentSolver solver = new BrentSolver();
+ solver.setAbsoluteAccuracy(1.0e-15);
+ solver.setRelativeAccuracy(1.0e-15);
+ optimizer.setLineSearchSolver(solver);
+ RealPointValuePair optimum1 =
+ optimizer.optimize(problem1, GoalType.MINIMIZE, new double[] { 0, 1, 2, 3 });
+ assertEquals(1.0, optimum1.getPoint()[0], 1.0e-5);
+ assertEquals(1.0, optimum1.getPoint()[1], 1.0e-5);
+ assertEquals(1.0, optimum1.getPoint()[2], 1.0e-5);
+ assertEquals(1.0, optimum1.getPoint()[3], 1.0e-5);
+
+ LinearProblem problem2 = new LinearProblem(new double[][] {
+ { 10.00, 7.00, 8.10, 7.20 },
+ { 7.08, 5.04, 6.00, 5.00 },
+ { 8.00, 5.98, 9.89, 9.00 },
+ { 6.99, 4.99, 9.00, 9.98 }
+ }, new double[] { 32, 23, 33, 31 });
+ RealPointValuePair optimum2 =
+ optimizer.optimize(problem2, GoalType.MINIMIZE, new double[] { 0, 1, 2, 3 });
+ assertEquals(-81.0, optimum2.getPoint()[0], 1.0e-1);
+ assertEquals(137.0, optimum2.getPoint()[1], 1.0e-1);
+ assertEquals(-34.0, optimum2.getPoint()[2], 1.0e-1);
+ assertEquals( 22.0, optimum2.getPoint()[3], 1.0e-1);
+
+ }
+
+ public void testMoreEstimatedParametersSimple() throws Exception {
+
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 3.0, 2.0, 0.0, 0.0 },
+ { 0.0, 1.0, -1.0, 1.0 },
+ { 2.0, 0.0, 1.0, 0.0 }
+ }, new double[] { 7.0, 3.0, 5.0 });
+
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 7, 6, 5, 4 });
+ assertEquals(0, optimum.getValue(), 1.0e-10);
+
+ }
+
+ public void testMoreEstimatedParametersUnsorted() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 1.0, 1.0, 1.0, 0.0 },
+ { 0.0, 0.0, 0.0, 0.0, 1.0, -1.0 },
+ { 0.0, 0.0, -1.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 0.0, -1.0, 1.0, 0.0 }
+ }, new double[] { 3.0, 12.0, -1.0, 7.0, 1.0 });
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 2, 2, 2, 2, 2, 2 });
+ assertEquals(0, optimum.getValue(), 1.0e-10);
+ }
+
+ public void testRedundantEquations() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 5.0 });
+
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 1, 1 });
+ assertEquals(2.0, optimum.getPoint()[0], 1.0e-8);
+ assertEquals(1.0, optimum.getPoint()[1], 1.0e-8);
+
+ }
+
+ public void testInconsistentEquations() throws Exception {
+ LinearProblem problem = new LinearProblem(new double[][] {
+ { 1.0, 1.0 },
+ { 1.0, -1.0 },
+ { 1.0, 3.0 }
+ }, new double[] { 3.0, 1.0, 4.0 });
+
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
+ RealPointValuePair optimum =
+ optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 1, 1 });
+ assertTrue(optimum.getValue() > 0.1);
+
+ }
+
+ public void testCircleFitting() throws Exception {
+ Circle circle = new Circle();
+ circle.addPoint( 30.0, 68.0);
+ circle.addPoint( 50.0, -6.0);
+ circle.addPoint(110.0, -20.0);
+ circle.addPoint( 35.0, 15.0);
+ circle.addPoint( 45.0, 97.0);
+ NonLinearConjugateGradientOptimizer optimizer =
+ new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
+ optimizer.setMaxIterations(100);
+ optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-30, 1.0e-30));
+ BrentSolver solver = new BrentSolver();
+ solver.setAbsoluteAccuracy(1.0e-13);
+ solver.setRelativeAccuracy(1.0e-15);
+ optimizer.setLineSearchSolver(solver);
+ RealPointValuePair optimum =
+ optimizer.optimize(circle, GoalType.MINIMIZE, new double[] { 98.680, 47.345 });
+ Point2D.Double center = new Point2D.Double(optimum.getPointRef()[0], optimum.getPointRef()[1]);
+ assertEquals(69.960161753, circle.getRadius(center), 1.0e-8);
+ assertEquals(96.075902096, center.x, 1.0e-8);
+ assertEquals(48.135167894, center.y, 1.0e-8);
+ }
+
+ private static class LinearProblem implements DifferentiableMultivariateRealFunction, Serializable {
+
+ private static final long serialVersionUID = 703247177355019415L;
+ final RealMatrix factors;
+ final double[] target;
+ public LinearProblem(double[][] factors, double[] target) {
+ this.factors = new BlockRealMatrix(factors);
+ this.target = target;
+ }
+
+ private double[] gradient(double[] point) {
+ double[] r = factors.operate(point);
+ for (int i = 0; i < r.length; ++i) {
+ r[i] -= target[i];
+ }
+ double[] p = factors.transpose().operate(r);
+ for (int i = 0; i < p.length; ++i) {
+ p[i] *= 2;
+ }
+ return p;
+ }
+
+ public double value(double[] variables) throws FunctionEvaluationException {
+ double[] y = factors.operate(variables);
+ double sum = 0;
+ for (int i = 0; i < y.length; ++i) {
+ double ri = y[i] - target[i];
+ sum += ri * ri;
+ }
+ return sum;
+ }
+
+ public MultivariateVectorialFunction gradient() {
+ return new MultivariateVectorialFunction() {
+ private static final long serialVersionUID = 2621997811350805819L;
+ public double[] value(double[] point) {
+ return gradient(point);
+ }
+ };
+ }
+
+ public MultivariateRealFunction partialDerivative(final int k) {
+ return new MultivariateRealFunction() {
+ private static final long serialVersionUID = -6186178619133562011L;
+ public double value(double[] point) {
+ return gradient(point)[k];
+ }
+ };
+ }
+
+ }
+
+ private static class Circle implements DifferentiableMultivariateRealFunction, Serializable {
+
+ private static final long serialVersionUID = -4711170319243817874L;
+
+ private ArrayList<Point2D.Double> points;
+
+ public Circle() {
+ points = new ArrayList<Point2D.Double>();
+ }
+
+ public void addPoint(double px, double py) {
+ points.add(new Point2D.Double(px, py));
+ }
+
+ public double getRadius(Point2D.Double center) {
+ double r = 0;
+ for (Point2D.Double point : points) {
+ r += point.distance(center);
+ }
+ return r / points.size();
+ }
+
+ private double[] gradient(double[] point) {
+
+ // optimal radius
+ Point2D.Double center = new Point2D.Double(point[0], point[1]);
+ double radius = getRadius(center);
+
+ // gradient of the sum of squared residuals
+ double dJdX = 0;
+ double dJdY = 0;
+ for (Point2D.Double pk : points) {
+ double dk = pk.distance(center);
+ dJdX += (center.x - pk.x) * (dk - radius) / dk;
+ dJdY += (center.y - pk.y) * (dk - radius) / dk;
+ }
+ dJdX *= 2;
+ dJdY *= 2;
+
+ return new double[] { dJdX, dJdY };
+
+ }
+
+ public double value(double[] variables)
+ throws IllegalArgumentException, FunctionEvaluationException {
+
+ Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
+ double radius = getRadius(center);
+
+ double sum = 0;
+ for (Point2D.Double point : points) {
+ double di = point.distance(center) - radius;
+ sum += di * di;
+ }
+
+ return sum;
+
+ }
+
+ public MultivariateVectorialFunction gradient() {
+ return new MultivariateVectorialFunction() {
+ private static final long serialVersionUID = 3174909643301201710L;
+ public double[] value(double[] point) {
+ return gradient(point);
+ }
+ };
+ }
+
+ public MultivariateRealFunction partialDerivative(final int k) {
+ return new MultivariateRealFunction() {
+ private static final long serialVersionUID = 3073956364104833888L;
+ public double value(double[] point) {
+ return gradient(point)[k];
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java b/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java
new file mode 100644
index 0000000..5bfbdd5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java
@@ -0,0 +1,458 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.junit.Assert;
+
+import java.util.ArrayList;
+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;
+import org.junit.Test;
+
+public class SimplexSolverTest {
+
+ @Test
+ public void testMath272() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 2, 2, 1 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 1, 0 }, Relationship.GEQ, 1));
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 1 }, Relationship.GEQ, 1));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 0 }, Relationship.GEQ, 1));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MINIMIZE, true);
+
+ Assert.assertEquals(0.0, solution.getPoint()[0], .0000001);
+ Assert.assertEquals(1.0, solution.getPoint()[1], .0000001);
+ Assert.assertEquals(1.0, solution.getPoint()[2], .0000001);
+ Assert.assertEquals(3.0, solution.getValue(), .0000001);
+ }
+
+ @Test
+ public void testMath286() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 0.8, 0.2, 0.7, 0.3, 0.6, 0.4 }, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 1, 0, 1, 0 }, Relationship.EQ, 23.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 0, 1, 0, 1 }, Relationship.EQ, 23.0));
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 0, 0, 0, 0 }, Relationship.GEQ, 10.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 0, 1, 0, 0, 0 }, Relationship.GEQ, 8.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 0, 0, 0, 1, 0 }, Relationship.GEQ, 5.0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+
+ Assert.assertEquals(25.8, solution.getValue(), .0000001);
+ Assert.assertEquals(23.0, solution.getPoint()[0] + solution.getPoint()[2] + solution.getPoint()[4], 0.0000001);
+ Assert.assertEquals(23.0, solution.getPoint()[1] + solution.getPoint()[3] + solution.getPoint()[5], 0.0000001);
+ Assert.assertTrue(solution.getPoint()[0] >= 10.0 - 0.0000001);
+ Assert.assertTrue(solution.getPoint()[2] >= 8.0 - 0.0000001);
+ Assert.assertTrue(solution.getPoint()[4] >= 5.0 - 0.0000001);
+ }
+
+ @Test
+ public void testDegeneracy() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 0.8, 0.7 }, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.LEQ, 18.0));
+ constraints.add(new LinearConstraint(new double[] { 1, 0 }, Relationship.GEQ, 10.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 1 }, Relationship.GEQ, 8.0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+ Assert.assertEquals(13.6, solution.getValue(), .0000001);
+ }
+
+ @Test
+ public void testMath288() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 7, 3, 0, 0 }, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 3, 0, -5, 0 }, Relationship.LEQ, 0.0));
+ constraints.add(new LinearConstraint(new double[] { 2, 0, 0, -5 }, Relationship.LEQ, 0.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 3, 0, -5 }, Relationship.LEQ, 0.0));
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 0, 0 }, Relationship.LEQ, 1.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 0, 0 }, Relationship.LEQ, 1.0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+ Assert.assertEquals(10.0, solution.getValue(), .0000001);
+ }
+
+ @Test
+ public void testMath290GEQ() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 1, 5 }, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 2, 0 }, Relationship.GEQ, -1.0));
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MINIMIZE, true);
+ Assert.assertEquals(0, solution.getValue(), .0000001);
+ Assert.assertEquals(0, solution.getPoint()[0], .0000001);
+ Assert.assertEquals(0, solution.getPoint()[1], .0000001);
+ }
+
+ @Test(expected=NoFeasibleSolutionException.class)
+ public void testMath290LEQ() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 1, 5 }, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 2, 0 }, Relationship.LEQ, -1.0));
+ SimplexSolver solver = new SimplexSolver();
+ solver.optimize(f, constraints, GoalType.MINIMIZE, true);
+ }
+
+ @Test
+ public void testMath293() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 0.8, 0.2, 0.7, 0.3, 0.4, 0.6}, 0 );
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 1, 0, 1, 0 }, Relationship.EQ, 30.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 0, 1, 0, 1 }, Relationship.EQ, 30.0));
+ constraints.add(new LinearConstraint(new double[] { 0.8, 0.2, 0.0, 0.0, 0.0, 0.0 }, Relationship.GEQ, 10.0));
+ constraints.add(new LinearConstraint(new double[] { 0.0, 0.0, 0.7, 0.3, 0.0, 0.0 }, Relationship.GEQ, 10.0));
+ constraints.add(new LinearConstraint(new double[] { 0.0, 0.0, 0.0, 0.0, 0.4, 0.6 }, Relationship.GEQ, 10.0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution1 = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+
+ Assert.assertEquals(15.7143, solution1.getPoint()[0], .0001);
+ Assert.assertEquals(0.0, solution1.getPoint()[1], .0001);
+ Assert.assertEquals(14.2857, solution1.getPoint()[2], .0001);
+ Assert.assertEquals(0.0, solution1.getPoint()[3], .0001);
+ Assert.assertEquals(0.0, solution1.getPoint()[4], .0001);
+ Assert.assertEquals(30.0, solution1.getPoint()[5], .0001);
+ Assert.assertEquals(40.57143, solution1.getValue(), .0001);
+
+ double valA = 0.8 * solution1.getPoint()[0] + 0.2 * solution1.getPoint()[1];
+ double valB = 0.7 * solution1.getPoint()[2] + 0.3 * solution1.getPoint()[3];
+ double valC = 0.4 * solution1.getPoint()[4] + 0.6 * solution1.getPoint()[5];
+
+ f = new LinearObjectiveFunction(new double[] { 0.8, 0.2, 0.7, 0.3, 0.4, 0.6}, 0 );
+ constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0, 1, 0, 1, 0 }, Relationship.EQ, 30.0));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 0, 1, 0, 1 }, Relationship.EQ, 30.0));
+ constraints.add(new LinearConstraint(new double[] { 0.8, 0.2, 0.0, 0.0, 0.0, 0.0 }, Relationship.GEQ, valA));
+ constraints.add(new LinearConstraint(new double[] { 0.0, 0.0, 0.7, 0.3, 0.0, 0.0 }, Relationship.GEQ, valB));
+ constraints.add(new LinearConstraint(new double[] { 0.0, 0.0, 0.0, 0.0, 0.4, 0.6 }, Relationship.GEQ, valC));
+
+ RealPointValuePair solution2 = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+ Assert.assertEquals(40.57143, solution2.getValue(), .0001);
+ }
+
+ @Test
+ public void testSimplexSolver() throws OptimizationException {
+ LinearObjectiveFunction f =
+ new LinearObjectiveFunction(new double[] { 15, 10 }, 7);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0 }, Relationship.LEQ, 2));
+ constraints.add(new LinearConstraint(new double[] { 0, 1 }, Relationship.LEQ, 3));
+ constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.EQ, 4));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ Assert.assertEquals(2.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(2.0, solution.getPoint()[1], 0.0);
+ Assert.assertEquals(57.0, solution.getValue(), 0.0);
+ }
+
+ @Test
+ public void testSingleVariableAndConstraint() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 3 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1 }, Relationship.LEQ, 10));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ Assert.assertEquals(10.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(30.0, solution.getValue(), 0.0);
+ }
+
+ /**
+ * With no artificial variables needed (no equals and no greater than
+ * constraints) we can go straight to Phase 2.
+ */
+ @Test
+ public void testModelWithNoArtificialVars() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 15, 10 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0 }, Relationship.LEQ, 2));
+ constraints.add(new LinearConstraint(new double[] { 0, 1 }, Relationship.LEQ, 3));
+ constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.LEQ, 4));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ Assert.assertEquals(2.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(2.0, solution.getPoint()[1], 0.0);
+ Assert.assertEquals(50.0, solution.getValue(), 0.0);
+ }
+
+ @Test
+ public void testMinimization() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { -2, 1 }, -5);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 2 }, Relationship.LEQ, 6));
+ constraints.add(new LinearConstraint(new double[] { 3, 2 }, Relationship.LEQ, 12));
+ constraints.add(new LinearConstraint(new double[] { 0, 1 }, Relationship.GEQ, 0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MINIMIZE, false);
+ Assert.assertEquals(4.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(0.0, solution.getPoint()[1], 0.0);
+ Assert.assertEquals(-13.0, solution.getValue(), 0.0);
+ }
+
+ @Test
+ public void testSolutionWithNegativeDecisionVariable() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { -2, 1 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.GEQ, 6));
+ constraints.add(new LinearConstraint(new double[] { 1, 2 }, Relationship.LEQ, 14));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ Assert.assertEquals(-2.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(8.0, solution.getPoint()[1], 0.0);
+ Assert.assertEquals(12.0, solution.getValue(), 0.0);
+ }
+
+ @Test(expected = NoFeasibleSolutionException.class)
+ public void testInfeasibleSolution() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 15 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1 }, Relationship.LEQ, 1));
+ constraints.add(new LinearConstraint(new double[] { 1 }, Relationship.GEQ, 3));
+
+ SimplexSolver solver = new SimplexSolver();
+ solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ }
+
+ @Test(expected = UnboundedSolutionException.class)
+ public void testUnboundedSolution() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 15, 10 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 0 }, Relationship.EQ, 2));
+
+ SimplexSolver solver = new SimplexSolver();
+ solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ }
+
+ @Test
+ public void testRestrictVariablesToNonNegative() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 409, 523, 70, 204, 339 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 43, 56, 345, 56, 5 }, Relationship.LEQ, 4567456));
+ constraints.add(new LinearConstraint(new double[] { 12, 45, 7, 56, 23 }, Relationship.LEQ, 56454));
+ constraints.add(new LinearConstraint(new double[] { 8, 768, 0, 34, 7456 }, Relationship.LEQ, 1923421));
+ constraints.add(new LinearConstraint(new double[] { 12342, 2342, 34, 678, 2342 }, Relationship.GEQ, 4356));
+ constraints.add(new LinearConstraint(new double[] { 45, 678, 76, 52, 23 }, Relationship.EQ, 456356));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+ Assert.assertEquals(2902.92783505155, solution.getPoint()[0], .0000001);
+ Assert.assertEquals(480.419243986254, solution.getPoint()[1], .0000001);
+ Assert.assertEquals(0.0, solution.getPoint()[2], .0000001);
+ Assert.assertEquals(0.0, solution.getPoint()[3], .0000001);
+ Assert.assertEquals(0.0, solution.getPoint()[4], .0000001);
+ Assert.assertEquals(1438556.7491409, solution.getValue(), .0000001);
+ }
+
+ @Test
+ public void testEpsilon() throws OptimizationException {
+ LinearObjectiveFunction f =
+ new LinearObjectiveFunction(new double[] { 10, 5, 1 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 9, 8, 0 }, Relationship.EQ, 17));
+ constraints.add(new LinearConstraint(new double[] { 0, 7, 8 }, Relationship.LEQ, 7));
+ constraints.add(new LinearConstraint(new double[] { 10, 0, 2 }, Relationship.LEQ, 10));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, false);
+ Assert.assertEquals(1.0, solution.getPoint()[0], 0.0);
+ Assert.assertEquals(1.0, solution.getPoint()[1], 0.0);
+ Assert.assertEquals(0.0, solution.getPoint()[2], 0.0);
+ Assert.assertEquals(15.0, solution.getValue(), 0.0);
+ }
+
+ @Test
+ public void testTrivialModel() throws OptimizationException {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 1, 1 }, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.EQ, 0));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MAXIMIZE, true);
+ Assert.assertEquals(0, solution.getValue(), .0000001);
+ }
+
+ @Test
+ public void testLargeModel() throws OptimizationException {
+ double[] objective = new double[] {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 12, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 12, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 12, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 12, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 12, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 12, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1};
+
+ LinearObjectiveFunction f = new LinearObjectiveFunction(objective, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(equationFromString(objective.length, "x0 + x1 + x2 + x3 - x12 = 0"));
+ constraints.add(equationFromString(objective.length, "x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 - x13 = 0"));
+ constraints.add(equationFromString(objective.length, "x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 >= 49"));
+ constraints.add(equationFromString(objective.length, "x0 + x1 + x2 + x3 >= 42"));
+ constraints.add(equationFromString(objective.length, "x14 + x15 + x16 + x17 - x26 = 0"));
+ constraints.add(equationFromString(objective.length, "x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 - x27 = 0"));
+ constraints.add(equationFromString(objective.length, "x14 + x15 + x16 + x17 - x12 = 0"));
+ constraints.add(equationFromString(objective.length, "x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 - x13 = 0"));
+ constraints.add(equationFromString(objective.length, "x28 + x29 + x30 + x31 - x40 = 0"));
+ constraints.add(equationFromString(objective.length, "x32 + x33 + x34 + x35 + x36 + x37 + x38 + x39 - x41 = 0"));
+ constraints.add(equationFromString(objective.length, "x32 + x33 + x34 + x35 + x36 + x37 + x38 + x39 >= 49"));
+ constraints.add(equationFromString(objective.length, "x28 + x29 + x30 + x31 >= 42"));
+ constraints.add(equationFromString(objective.length, "x42 + x43 + x44 + x45 - x54 = 0"));
+ constraints.add(equationFromString(objective.length, "x46 + x47 + x48 + x49 + x50 + x51 + x52 + x53 - x55 = 0"));
+ constraints.add(equationFromString(objective.length, "x42 + x43 + x44 + x45 - x40 = 0"));
+ constraints.add(equationFromString(objective.length, "x46 + x47 + x48 + x49 + x50 + x51 + x52 + x53 - x41 = 0"));
+ constraints.add(equationFromString(objective.length, "x56 + x57 + x58 + x59 - x68 = 0"));
+ constraints.add(equationFromString(objective.length, "x60 + x61 + x62 + x63 + x64 + x65 + x66 + x67 - x69 = 0"));
+ constraints.add(equationFromString(objective.length, "x60 + x61 + x62 + x63 + x64 + x65 + x66 + x67 >= 51"));
+ constraints.add(equationFromString(objective.length, "x56 + x57 + x58 + x59 >= 44"));
+ constraints.add(equationFromString(objective.length, "x70 + x71 + x72 + x73 - x82 = 0"));
+ constraints.add(equationFromString(objective.length, "x74 + x75 + x76 + x77 + x78 + x79 + x80 + x81 - x83 = 0"));
+ constraints.add(equationFromString(objective.length, "x70 + x71 + x72 + x73 - x68 = 0"));
+ constraints.add(equationFromString(objective.length, "x74 + x75 + x76 + x77 + x78 + x79 + x80 + x81 - x69 = 0"));
+ constraints.add(equationFromString(objective.length, "x84 + x85 + x86 + x87 - x96 = 0"));
+ constraints.add(equationFromString(objective.length, "x88 + x89 + x90 + x91 + x92 + x93 + x94 + x95 - x97 = 0"));
+ constraints.add(equationFromString(objective.length, "x88 + x89 + x90 + x91 + x92 + x93 + x94 + x95 >= 51"));
+ constraints.add(equationFromString(objective.length, "x84 + x85 + x86 + x87 >= 44"));
+ constraints.add(equationFromString(objective.length, "x98 + x99 + x100 + x101 - x110 = 0"));
+ constraints.add(equationFromString(objective.length, "x102 + x103 + x104 + x105 + x106 + x107 + x108 + x109 - x111 = 0"));
+ constraints.add(equationFromString(objective.length, "x98 + x99 + x100 + x101 - x96 = 0"));
+ constraints.add(equationFromString(objective.length, "x102 + x103 + x104 + x105 + x106 + x107 + x108 + x109 - x97 = 0"));
+ constraints.add(equationFromString(objective.length, "x112 + x113 + x114 + x115 - x124 = 0"));
+ constraints.add(equationFromString(objective.length, "x116 + x117 + x118 + x119 + x120 + x121 + x122 + x123 - x125 = 0"));
+ constraints.add(equationFromString(objective.length, "x116 + x117 + x118 + x119 + x120 + x121 + x122 + x123 >= 49"));
+ constraints.add(equationFromString(objective.length, "x112 + x113 + x114 + x115 >= 42"));
+ constraints.add(equationFromString(objective.length, "x126 + x127 + x128 + x129 - x138 = 0"));
+ constraints.add(equationFromString(objective.length, "x130 + x131 + x132 + x133 + x134 + x135 + x136 + x137 - x139 = 0"));
+ constraints.add(equationFromString(objective.length, "x126 + x127 + x128 + x129 - x124 = 0"));
+ constraints.add(equationFromString(objective.length, "x130 + x131 + x132 + x133 + x134 + x135 + x136 + x137 - x125 = 0"));
+ constraints.add(equationFromString(objective.length, "x140 + x141 + x142 + x143 - x152 = 0"));
+ constraints.add(equationFromString(objective.length, "x144 + x145 + x146 + x147 + x148 + x149 + x150 + x151 - x153 = 0"));
+ constraints.add(equationFromString(objective.length, "x144 + x145 + x146 + x147 + x148 + x149 + x150 + x151 >= 59"));
+ constraints.add(equationFromString(objective.length, "x140 + x141 + x142 + x143 >= 42"));
+ constraints.add(equationFromString(objective.length, "x154 + x155 + x156 + x157 - x166 = 0"));
+ constraints.add(equationFromString(objective.length, "x158 + x159 + x160 + x161 + x162 + x163 + x164 + x165 - x167 = 0"));
+ constraints.add(equationFromString(objective.length, "x154 + x155 + x156 + x157 - x152 = 0"));
+ constraints.add(equationFromString(objective.length, "x158 + x159 + x160 + x161 + x162 + x163 + x164 + x165 - x153 = 0"));
+ constraints.add(equationFromString(objective.length, "x83 + x82 - x168 = 0"));
+ constraints.add(equationFromString(objective.length, "x111 + x110 - x169 = 0"));
+ constraints.add(equationFromString(objective.length, "x170 - x182 = 0"));
+ constraints.add(equationFromString(objective.length, "x171 - x183 = 0"));
+ constraints.add(equationFromString(objective.length, "x172 - x184 = 0"));
+ constraints.add(equationFromString(objective.length, "x173 - x185 = 0"));
+ constraints.add(equationFromString(objective.length, "x174 - x186 = 0"));
+ constraints.add(equationFromString(objective.length, "x175 + x176 - x187 = 0"));
+ constraints.add(equationFromString(objective.length, "x177 - x188 = 0"));
+ constraints.add(equationFromString(objective.length, "x178 - x189 = 0"));
+ constraints.add(equationFromString(objective.length, "x179 - x190 = 0"));
+ constraints.add(equationFromString(objective.length, "x180 - x191 = 0"));
+ constraints.add(equationFromString(objective.length, "x181 - x192 = 0"));
+ constraints.add(equationFromString(objective.length, "x170 - x26 = 0"));
+ constraints.add(equationFromString(objective.length, "x171 - x27 = 0"));
+ constraints.add(equationFromString(objective.length, "x172 - x54 = 0"));
+ constraints.add(equationFromString(objective.length, "x173 - x55 = 0"));
+ constraints.add(equationFromString(objective.length, "x174 - x168 = 0"));
+ constraints.add(equationFromString(objective.length, "x177 - x169 = 0"));
+ constraints.add(equationFromString(objective.length, "x178 - x138 = 0"));
+ constraints.add(equationFromString(objective.length, "x179 - x139 = 0"));
+ constraints.add(equationFromString(objective.length, "x180 - x166 = 0"));
+ constraints.add(equationFromString(objective.length, "x181 - x167 = 0"));
+ constraints.add(equationFromString(objective.length, "x193 - x205 = 0"));
+ constraints.add(equationFromString(objective.length, "x194 - x206 = 0"));
+ constraints.add(equationFromString(objective.length, "x195 - x207 = 0"));
+ constraints.add(equationFromString(objective.length, "x196 - x208 = 0"));
+ constraints.add(equationFromString(objective.length, "x197 - x209 = 0"));
+ constraints.add(equationFromString(objective.length, "x198 + x199 - x210 = 0"));
+ constraints.add(equationFromString(objective.length, "x200 - x211 = 0"));
+ constraints.add(equationFromString(objective.length, "x201 - x212 = 0"));
+ constraints.add(equationFromString(objective.length, "x202 - x213 = 0"));
+ constraints.add(equationFromString(objective.length, "x203 - x214 = 0"));
+ constraints.add(equationFromString(objective.length, "x204 - x215 = 0"));
+ constraints.add(equationFromString(objective.length, "x193 - x182 = 0"));
+ constraints.add(equationFromString(objective.length, "x194 - x183 = 0"));
+ constraints.add(equationFromString(objective.length, "x195 - x184 = 0"));
+ constraints.add(equationFromString(objective.length, "x196 - x185 = 0"));
+ constraints.add(equationFromString(objective.length, "x197 - x186 = 0"));
+ constraints.add(equationFromString(objective.length, "x198 + x199 - x187 = 0"));
+ constraints.add(equationFromString(objective.length, "x200 - x188 = 0"));
+ constraints.add(equationFromString(objective.length, "x201 - x189 = 0"));
+ constraints.add(equationFromString(objective.length, "x202 - x190 = 0"));
+ constraints.add(equationFromString(objective.length, "x203 - x191 = 0"));
+ constraints.add(equationFromString(objective.length, "x204 - x192 = 0"));
+
+ SimplexSolver solver = new SimplexSolver();
+ RealPointValuePair solution = solver.optimize(f, constraints, GoalType.MINIMIZE, true);
+ Assert.assertEquals(7518.0, solution.getValue(), .0000001);
+ }
+
+ /**
+ * Converts a test string to a {@link LinearConstraint}.
+ * Ex: x0 + x1 + x2 + x3 - x12 = 0
+ */
+ private LinearConstraint equationFromString(int numCoefficients, String s) {
+ Relationship relationship;
+ if (s.contains(">=")) {
+ relationship = Relationship.GEQ;
+ } else if (s.contains("<=")) {
+ relationship = Relationship.LEQ;
+ } else if (s.contains("=")) {
+ relationship = Relationship.EQ;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ String[] equationParts = s.split("[>|<]?=");
+ double rhs = Double.parseDouble(equationParts[1].trim());
+
+ double[] lhs = new double[numCoefficients];
+ String left = equationParts[0].replaceAll(" ?x", "");
+ String[] coefficients = left.split(" ");
+ for (String coefficient : coefficients) {
+ double value = coefficient.charAt(0) == '-' ? -1 : 1;
+ int index = Integer.parseInt(coefficient.replaceFirst("[+|-]", "").trim());
+ lhs[index] = value;
+ }
+ return new LinearConstraint(lhs, relationship, rhs);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/linear/SimplexTableauTest.java b/src/test/java/org/apache/commons/math/optimization/linear/SimplexTableauTest.java
new file mode 100644
index 0000000..d41c238
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/linear/SimplexTableauTest.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.optimization.linear;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.optimization.GoalType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SimplexTableauTest {
+
+ @Test
+ public void testInitialization() {
+ LinearObjectiveFunction f = createFunction();
+ Collection<LinearConstraint> constraints = createConstraints();
+ SimplexTableau tableau =
+ new SimplexTableau(f, constraints, GoalType.MAXIMIZE, false, 1.0e-6);
+ double[][] expectedInitialTableau = {
+ {-1, 0, -1, -1, 2, 0, 0, 0, -4},
+ { 0, 1, -15, -10, 25, 0, 0, 0, 0},
+ { 0, 0, 1, 0, -1, 1, 0, 0, 2},
+ { 0, 0, 0, 1, -1, 0, 1, 0, 3},
+ { 0, 0, 1, 1, -2, 0, 0, 1, 4}
+ };
+ assertMatrixEquals(expectedInitialTableau, tableau.getData());
+ }
+
+ @Test
+ public void testDropPhase1Objective() {
+ LinearObjectiveFunction f = createFunction();
+ Collection<LinearConstraint> constraints = createConstraints();
+ SimplexTableau tableau =
+ new SimplexTableau(f, constraints, GoalType.MAXIMIZE, false, 1.0e-6);
+ double[][] expectedTableau = {
+ { 1, -15, -10, 0, 0, 0, 0},
+ { 0, 1, 0, 1, 0, 0, 2},
+ { 0, 0, 1, 0, 1, 0, 3},
+ { 0, 1, 1, 0, 0, 1, 4}
+ };
+ tableau.dropPhase1Objective();
+ assertMatrixEquals(expectedTableau, tableau.getData());
+ }
+
+ @Test
+ public void testTableauWithNoArtificialVars() {
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] {15, 10}, 0);
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] {1, 0}, Relationship.LEQ, 2));
+ constraints.add(new LinearConstraint(new double[] {0, 1}, Relationship.LEQ, 3));
+ constraints.add(new LinearConstraint(new double[] {1, 1}, Relationship.LEQ, 4));
+ SimplexTableau tableau =
+ new SimplexTableau(f, constraints, GoalType.MAXIMIZE, false, 1.0e-6);
+ double[][] initialTableau = {
+ {1, -15, -10, 25, 0, 0, 0, 0},
+ {0, 1, 0, -1, 1, 0, 0, 2},
+ {0, 0, 1, -1, 0, 1, 0, 3},
+ {0, 1, 1, -2, 0, 0, 1, 4}
+ };
+ assertMatrixEquals(initialTableau, tableau.getData());
+ }
+
+ @Test
+ public void testSerial() {
+ LinearObjectiveFunction f = createFunction();
+ Collection<LinearConstraint> constraints = createConstraints();
+ SimplexTableau tableau =
+ new SimplexTableau(f, constraints, GoalType.MAXIMIZE, false, 1.0e-6);
+ Assert.assertEquals(tableau, TestUtils.serializeAndRecover(tableau));
+ }
+
+ private LinearObjectiveFunction createFunction() {
+ return new LinearObjectiveFunction(new double[] {15, 10}, 0);
+ }
+
+ private Collection<LinearConstraint> createConstraints() {
+ Collection<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
+ constraints.add(new LinearConstraint(new double[] {1, 0}, Relationship.LEQ, 2));
+ constraints.add(new LinearConstraint(new double[] {0, 1}, Relationship.LEQ, 3));
+ constraints.add(new LinearConstraint(new double[] {1, 1}, Relationship.EQ, 4));
+ return constraints;
+ }
+
+ private void assertMatrixEquals(double[][] expected, double[][] result) {
+ Assert.assertEquals("Wrong number of rows.", expected.length, result.length);
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertEquals("Wrong number of columns.", expected[i].length, result[i].length);
+ for (int j = 0; j < expected[i].length; j++) {
+ Assert.assertEquals("Wrong value at position [" + i + "," + j + "]", expected[i][j], result[i][j], 1.0e-15);
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/univariate/BracketFinderTest.java b/src/test/java/org/apache/commons/math/optimization/univariate/BracketFinderTest.java
new file mode 100644
index 0000000..2acd626
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/univariate/BracketFinderTest.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.optimization.univariate;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.optimization.GoalType;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BracketFinderTest {
+
+ @Test
+ public void testCubicMin() throws MathException {
+ final BracketFinder bFind = new BracketFinder();
+ final UnivariateRealFunction func = new UnivariateRealFunction() {
+ public double value(double x) {
+ if (x < -2) {
+ return value(-2);
+ }
+ else {
+ return (x - 1) * (x + 2) * (x + 3);
+ }
+ }
+ };
+
+ bFind.search(func, GoalType.MINIMIZE, -2 , -1);
+ final double tol = 1e-15;
+ // Comparing with results computed in Python.
+ Assert.assertEquals(-2, bFind.getLo(), tol);
+ Assert.assertEquals(-1, bFind.getMid(), tol);
+ Assert.assertEquals(0.61803399999999997, bFind.getHi(), tol);
+ }
+
+ @Test
+ public void testCubicMax() throws MathException {
+ final BracketFinder bFind = new BracketFinder();
+ final UnivariateRealFunction func = new UnivariateRealFunction() {
+ public double value(double x) {
+ if (x < -2) {
+ return value(-2);
+ }
+ else {
+ return -(x - 1) * (x + 2) * (x + 3);
+ }
+ }
+ };
+
+ bFind.search(func, GoalType.MAXIMIZE, -2 , -1);
+ final double tol = 1e-15;
+ Assert.assertEquals(-2, bFind.getLo(), tol);
+ Assert.assertEquals(-1, bFind.getMid(), tol);
+ Assert.assertEquals(0.61803399999999997, bFind.getHi(), tol);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java
new file mode 100644
index 0000000..2059289
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/optimization/univariate/BrentOptimizerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.optimization.univariate;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.MaxIterationsExceededException;
+import org.apache.commons.math.exception.NoDataException;
+import org.apache.commons.math.analysis.QuinticFunction;
+import org.apache.commons.math.analysis.SinFunction;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.optimization.GoalType;
+import org.apache.commons.math.optimization.UnivariateRealOptimizer;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (Sat, 05 Sep 2009) $
+ */
+public final class BrentOptimizerTest {
+
+ @Test
+ public void testSinMin() throws MathException {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealOptimizer minimizer = new BrentOptimizer();
+ minimizer.setMaxEvaluations(200);
+ assertEquals(200, minimizer.getMaxEvaluations());
+ try {
+ minimizer.getResult();
+ fail("an exception should have been thrown");
+ } catch (NoDataException ise) {
+ // expected
+ }
+ assertEquals(3 * FastMath.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 4, 5), 10 * minimizer.getRelativeAccuracy());
+ assertTrue(minimizer.getIterationCount() <= 50);
+ assertEquals(3 * FastMath.PI / 2, minimizer.optimize(f, GoalType.MINIMIZE, 1, 5), 10 * minimizer.getRelativeAccuracy());
+ assertTrue(minimizer.getIterationCount() <= 50);
+ assertTrue(minimizer.getEvaluations() <= 100);
+ assertTrue(minimizer.getEvaluations() >= 15);
+ minimizer.setMaxEvaluations(10);
+ try {
+ minimizer.optimize(f, GoalType.MINIMIZE, 4, 5);
+ fail("an exception should have been thrown");
+ } catch (FunctionEvaluationException mue) {
+ // expected
+ } catch (Exception e) {
+ fail("wrong exception caught");
+ }
+ }
+
+ @Test
+ public void testQuinticMin() throws MathException {
+ // The function has local minima at -0.27195613 and 0.82221643.
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealOptimizer minimizer = new BrentOptimizer();
+ assertEquals(-0.27195613, minimizer.optimize(f, GoalType.MINIMIZE, -0.3, -0.2), 1.0e-8);
+ assertEquals( 0.82221643, minimizer.optimize(f, GoalType.MINIMIZE, 0.3, 0.9), 1.0e-8);
+ assertTrue(minimizer.getIterationCount() <= 50);
+
+ // search in a large interval
+ assertEquals(-0.27195613, minimizer.optimize(f, GoalType.MINIMIZE, -1.0, 0.2), 1.0e-8);
+ assertTrue(minimizer.getIterationCount() <= 50);
+ }
+
+ @Test
+ public void testQuinticMinStatistics() throws MathException {
+ // The function has local minima at -0.27195613 and 0.82221643.
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealOptimizer minimizer = new BrentOptimizer();
+ minimizer.setRelativeAccuracy(1e-10);
+ minimizer.setAbsoluteAccuracy(1e-11);
+
+ final DescriptiveStatistics[] stat = new DescriptiveStatistics[3];
+ for (int i = 0; i < stat.length; i++) {
+ stat[i] = new DescriptiveStatistics();
+ }
+
+ final double min = -0.75;
+ final double max = 0.25;
+ final int nSamples = 200;
+ final double delta = (max - min) / nSamples;
+ for (int i = 0; i < nSamples; i++) {
+ final double start = min + i * delta;
+ stat[0].addValue(minimizer.optimize(f, GoalType.MINIMIZE, min, max, start));
+ stat[1].addValue(minimizer.getIterationCount());
+ stat[2].addValue(minimizer.getEvaluations());
+ }
+
+ final double meanOptValue = stat[0].getMean();
+ final double medianIter = stat[1].getPercentile(50);
+ final double medianEval = stat[2].getPercentile(50);
+ assertTrue(meanOptValue > -0.27195612812 && meanOptValue < -0.27195612811);
+ assertEquals(medianIter, 17, FastMath.ulp(1d));
+ assertEquals(medianEval, 18, FastMath.ulp(1d));
+ }
+
+ @Test
+ public void testQuinticMax() throws MathException {
+ // The quintic function has zeros at 0, +-0.5 and +-1.
+ // The function has a local maximum at 0.27195613.
+ UnivariateRealFunction f = new QuinticFunction();
+ UnivariateRealOptimizer minimizer = new BrentOptimizer();
+ assertEquals(0.27195613, minimizer.optimize(f, GoalType.MAXIMIZE, 0.2, 0.3), 1.0e-8);
+ minimizer.setMaximalIterationCount(5);
+ try {
+ minimizer.optimize(f, GoalType.MAXIMIZE, 0.2, 0.3);
+ fail("an exception should have been thrown");
+ } catch (MaxIterationsExceededException miee) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testMinEndpoints() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ UnivariateRealOptimizer solver = new BrentOptimizer();
+
+ solver.setRelativeAccuracy(1e-8);
+
+ // endpoint is minimum
+ double result = solver.optimize(f, GoalType.MINIMIZE, 3 * FastMath.PI / 2, 5);
+ assertEquals(3 * FastMath.PI / 2, result, 10 * solver.getRelativeAccuracy());
+
+ result = solver.optimize(f, GoalType.MINIMIZE, 4, 3 * FastMath.PI / 2);
+ assertEquals(3 * FastMath.PI / 2, result, 10 * solver.getRelativeAccuracy());
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/random/AbstractRandomGeneratorTest.java b/src/test/java/org/apache/commons/math/random/AbstractRandomGeneratorTest.java
new file mode 100644
index 0000000..fae0941
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/AbstractRandomGeneratorTest.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.random;
+
+import org.apache.commons.math.stat.Frequency;
+
+
+/**
+ * Test cases for the AbstractRandomGenerator class
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class AbstractRandomGeneratorTest extends RandomDataTest {
+
+ protected TestRandomGenerator testGenerator = new TestRandomGenerator();
+
+ public AbstractRandomGeneratorTest(String name) {
+ super(name);
+ randomData = new RandomDataImpl(testGenerator);
+ }
+
+ @Override
+ public void testNextInt() {
+ try {
+ testGenerator.nextInt(-1);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency freq = new Frequency();
+ int value = 0;
+ for (int i=0; i<smallSampleSize; i++) {
+ value = testGenerator.nextInt(4);
+ assertTrue("nextInt range",(value >= 0) && (value <= 3));
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i=0; i<4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
+ * Change to 11.34 for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected,observed) < 16.27);
+ }
+
+ @Override
+ public void testNextLong() {
+ long q1 = Long.MAX_VALUE/4;
+ long q2 = 2 * q1;
+ long q3 = 3 * q1;
+
+ Frequency freq = new Frequency();
+ long val = 0;
+ int value = 0;
+ for (int i=0; i<smallSampleSize; i++) {
+ val = testGenerator.nextLong();
+ if (val < q1) {
+ value = 0;
+ } else if (val < q2) {
+ value = 1;
+ } else if (val < q3) {
+ value = 2;
+ } else {
+ value = 3;
+ }
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i=0; i<4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
+ * Change to 11.34 for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected,observed) < 16.27);
+ }
+
+ public void testNextBoolean() {
+ long halfSampleSize = smallSampleSize / 2;
+ double[] expected = {halfSampleSize, halfSampleSize};
+ long[] observed = new long[2];
+ for (int i=0; i<smallSampleSize; i++) {
+ if (testGenerator.nextBoolean()) {
+ observed[0]++;
+ } else {
+ observed[1]++;
+ }
+ }
+ /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
+ * Change to 6.635 for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected,observed) < 10.828);
+ }
+
+ public void testNextFloat() {
+ Frequency freq = new Frequency();
+ float val = 0;
+ int value = 0;
+ for (int i=0; i<smallSampleSize; i++) {
+ val = testGenerator.nextFloat();
+ if (val < 0.25) {
+ value = 0;
+ } else if (val < 0.5) {
+ value = 1;
+ } else if (val < 0.75) {
+ value = 2;
+ } else {
+ value = 3;
+ }
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i=0; i<4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
+ * Change to 11.34 for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected,observed) < 16.27);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/random/CorrelatedRandomVectorGeneratorTest.java b/src/test/java/org/apache/commons/math/random/CorrelatedRandomVectorGeneratorTest.java
new file mode 100644
index 0000000..842c2bf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/CorrelatedRandomVectorGeneratorTest.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;
+
+import junit.framework.TestCase;
+
+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.stat.descriptive.moment.VectorialCovariance;
+import org.apache.commons.math.stat.descriptive.moment.VectorialMean;
+import org.apache.commons.math.util.FastMath;
+
+public class CorrelatedRandomVectorGeneratorTest
+extends TestCase {
+
+ public CorrelatedRandomVectorGeneratorTest(String name) {
+ super(name);
+ mean = null;
+ covariance = null;
+ generator = null;
+ }
+
+ public void testRank() {
+ assertEquals(3, generator.getRank());
+ }
+
+ public void testMath226()
+ throws DimensionMismatchException, NotPositiveDefiniteMatrixException {
+ double[] mean = { 1, 1, 10, 1 };
+ double[][] cov = {
+ { 1, 3, 2, 6 },
+ { 3, 13, 16, 2 },
+ { 2, 16, 38, -1 },
+ { 6, 2, -1, 197 }
+ };
+ RealMatrix covRM = MatrixUtils.createRealMatrix(cov);
+ JDKRandomGenerator jg = new JDKRandomGenerator();
+ jg.setSeed(5322145245211l);
+ NormalizedRandomGenerator rg = new GaussianRandomGenerator(jg);
+ CorrelatedRandomVectorGenerator sg =
+ new CorrelatedRandomVectorGenerator(mean, covRM, 0.00001, rg);
+
+ for (int i = 0; i < 10; i++) {
+ double[] generated = sg.nextVector();
+ assertTrue(FastMath.abs(generated[0] - 1) > 0.1);
+ }
+
+ }
+
+ public void testRootMatrix() {
+ RealMatrix b = generator.getRootMatrix();
+ RealMatrix bbt = b.multiply(b.transpose());
+ for (int i = 0; i < covariance.getRowDimension(); ++i) {
+ for (int j = 0; j < covariance.getColumnDimension(); ++j) {
+ assertEquals(covariance.getEntry(i, j), bbt.getEntry(i, j), 1.0e-12);
+ }
+ }
+ }
+
+ public void testMeanAndCovariance() throws DimensionMismatchException {
+
+ VectorialMean meanStat = new VectorialMean(mean.length);
+ VectorialCovariance covStat = new VectorialCovariance(mean.length, true);
+ for (int i = 0; i < 5000; ++i) {
+ double[] v = generator.nextVector();
+ meanStat.increment(v);
+ covStat.increment(v);
+ }
+
+ double[] estimatedMean = meanStat.getResult();
+ RealMatrix estimatedCovariance = covStat.getResult();
+ for (int i = 0; i < estimatedMean.length; ++i) {
+ assertEquals(mean[i], estimatedMean[i], 0.07);
+ for (int j = 0; j <= i; ++j) {
+ assertEquals(covariance.getEntry(i, j),
+ estimatedCovariance.getEntry(i, j),
+ 0.1 * (1.0 + FastMath.abs(mean[i])) * (1.0 + FastMath.abs(mean[j])));
+ }
+ }
+
+ }
+
+ @Override
+ public void setUp() {
+ try {
+ mean = new double[] { 0.0, 1.0, -3.0, 2.3};
+
+ RealMatrix b = MatrixUtils.createRealMatrix(4, 3);
+ int counter = 0;
+ for (int i = 0; i < b.getRowDimension(); ++i) {
+ for (int j = 0; j < b.getColumnDimension(); ++j) {
+ b.setEntry(i, j, 1.0 + 0.1 * ++counter);
+ }
+ }
+ RealMatrix bbt = b.multiply(b.transpose());
+ covariance = MatrixUtils.createRealMatrix(mean.length, mean.length);
+ for (int i = 0; i < covariance.getRowDimension(); ++i) {
+ covariance.setEntry(i, i, bbt.getEntry(i, i));
+ for (int j = 0; j < covariance.getColumnDimension(); ++j) {
+ double s = bbt.getEntry(i, j);
+ covariance.setEntry(i, j, s);
+ covariance.setEntry(j, i, s);
+ }
+ }
+
+ RandomGenerator rg = new JDKRandomGenerator();
+ rg.setSeed(17399225432l);
+ GaussianRandomGenerator rawGenerator = new GaussianRandomGenerator(rg);
+ generator = new CorrelatedRandomVectorGenerator(mean,
+ covariance,
+ 1.0e-12 * covariance.getNorm(),
+ rawGenerator);
+ } catch (DimensionMismatchException e) {
+ fail(e.getMessage());
+ } catch (NotPositiveDefiniteMatrixException e) {
+ fail("not positive definite matrix");
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ mean = null;
+ covariance = null;
+ generator = null;
+ }
+
+ private double[] mean;
+ private RealMatrix covariance;
+ private CorrelatedRandomVectorGenerator generator;
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/EmpiricalDistributionTest.java b/src/test/java/org/apache/commons/math/random/EmpiricalDistributionTest.java
new file mode 100644
index 0000000..dfacf30
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/EmpiricalDistributionTest.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.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+
+import org.apache.commons.math.RetryTestCase;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+
+/**
+ * Test cases for the EmpiricalDistribution class
+ *
+ * @version $Revision: 1003907 $ $Date: 2010-10-03 00:23:34 +0200 (dim. 03 oct. 2010) $
+ */
+
+public final class EmpiricalDistributionTest extends RetryTestCase {
+
+ protected EmpiricalDistribution empiricalDistribution = null;
+ protected EmpiricalDistribution empiricalDistribution2 = null;
+ protected File file = null;
+ protected URL url = null;
+ protected double[] dataArray = null;
+
+ public EmpiricalDistributionTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws IOException {
+ empiricalDistribution = new EmpiricalDistributionImpl(100);
+ url = getClass().getResource("testData.txt");
+
+ empiricalDistribution2 = new EmpiricalDistributionImpl(100);
+ BufferedReader in =
+ new BufferedReader(new InputStreamReader(
+ url.openStream()));
+ String str = null;
+ ArrayList<Double> list = new ArrayList<Double>();
+ while ((str = in.readLine()) != null) {
+ list.add(Double.valueOf(str));
+ }
+ in.close();
+ in = null;
+
+ dataArray = new double[list.size()];
+ int i = 0;
+ for (Double data : list) {
+ dataArray[i] = data.doubleValue();
+ i++;
+ }
+ }
+
+ /**
+ * Test EmpiricalDistrbution.load() using sample data file.<br>
+ * Check that the sampleCount, mu and sigma match data in
+ * the sample data file.
+ */
+ public void testLoad() throws Exception {
+ empiricalDistribution.load(url);
+ // testData File has 10000 values, with mean ~ 5.0, std dev ~ 1
+ // Make sure that loaded distribution matches this
+ assertEquals(empiricalDistribution.getSampleStats().getN(),1000,10E-7);
+ //TODO: replace with statistical tests
+ assertEquals
+ (empiricalDistribution.getSampleStats().getMean(),
+ 5.069831575018909,10E-7);
+ assertEquals
+ (empiricalDistribution.getSampleStats().getStandardDeviation(),
+ 1.0173699343977738,10E-7);
+ }
+
+ /**
+ * Test EmpiricalDistrbution.load(double[]) using data taken from
+ * sample data file.<br>
+ * Check that the sampleCount, mu and sigma match data in
+ * the sample data file.
+ */
+ public void testDoubleLoad() throws Exception {
+ empiricalDistribution2.load(dataArray);
+ // testData File has 10000 values, with mean ~ 5.0, std dev ~ 1
+ // Make sure that loaded distribution matches this
+ assertEquals(empiricalDistribution2.getSampleStats().getN(),1000,10E-7);
+ //TODO: replace with statistical tests
+ assertEquals
+ (empiricalDistribution2.getSampleStats().getMean(),
+ 5.069831575018909,10E-7);
+ assertEquals
+ (empiricalDistribution2.getSampleStats().getStandardDeviation(),
+ 1.0173699343977738,10E-7);
+
+ double[] bounds = ((EmpiricalDistributionImpl) empiricalDistribution2).getGeneratorUpperBounds();
+ assertEquals(bounds.length, 100);
+ assertEquals(bounds[99], 1.0, 10e-12);
+
+ }
+
+ /**
+ * Generate 1000 random values and make sure they look OK.<br>
+ * Note that there is a non-zero (but very small) probability that
+ * these tests will fail even if the code is working as designed.
+ */
+ public void testNext() throws Exception {
+ tstGen(0.1);
+ tstDoubleGen(0.1);
+ }
+
+ /**
+ * Make sure exception thrown if digest getNext is attempted
+ * before loading empiricalDistribution.
+ */
+ public void testNexFail() {
+ try {
+ empiricalDistribution.getNextValue();
+ empiricalDistribution2.getNextValue();
+ fail("Expecting IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ /**
+ * Make sure we can handle a grid size that is too fine
+ */
+ public void testGridTooFine() throws Exception {
+ empiricalDistribution = new EmpiricalDistributionImpl(1001);
+ tstGen(0.1);
+ empiricalDistribution2 = new EmpiricalDistributionImpl(1001);
+ tstDoubleGen(0.1);
+ }
+
+ /**
+ * How about too fat?
+ */
+ public void testGridTooFat() throws Exception {
+ empiricalDistribution = new EmpiricalDistributionImpl(1);
+ tstGen(5); // ridiculous tolerance; but ridiculous grid size
+ // really just checking to make sure we do not bomb
+ empiricalDistribution2 = new EmpiricalDistributionImpl(1);
+ tstDoubleGen(5);
+ }
+
+ /**
+ * Test bin index overflow problem (BZ 36450)
+ */
+ public void testBinIndexOverflow() throws Exception {
+ double[] x = new double[] {9474.94326071674, 2080107.8865462579};
+ new EmpiricalDistributionImpl().load(x);
+ }
+
+ public void testSerialization() {
+ // Empty
+ EmpiricalDistribution dist = new EmpiricalDistributionImpl();
+ EmpiricalDistribution dist2 = (EmpiricalDistribution) TestUtils.serializeAndRecover(dist);
+ verifySame(dist, dist2);
+
+ // Loaded
+ empiricalDistribution2.load(dataArray);
+ dist2 = (EmpiricalDistribution) TestUtils.serializeAndRecover(empiricalDistribution2);
+ verifySame(empiricalDistribution2, dist2);
+ }
+
+ public void testLoadNullDoubleArray() {
+ EmpiricalDistribution dist = new EmpiricalDistributionImpl();
+ try {
+ dist.load((double[]) null);
+ fail("load((double[]) null) expected NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testLoadNullURL() throws Exception {
+ EmpiricalDistribution dist = new EmpiricalDistributionImpl();
+ try {
+ dist.load((URL) null);
+ fail("load((URL) null) expected NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testLoadNullFile() throws Exception {
+ EmpiricalDistribution dist = new EmpiricalDistributionImpl();
+ try {
+ dist.load((File) null);
+ fail("load((File) null) expected NullPointerException");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ /**
+ * MATH-298
+ */
+ public void testGetBinUpperBounds() {
+ double[] testData = {0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10};
+ EmpiricalDistributionImpl dist = new EmpiricalDistributionImpl(5);
+ dist.load(testData);
+ double[] expectedBinUpperBounds = {2, 4, 6, 8, 10};
+ double[] expectedGeneratorUpperBounds = {4d/13d, 7d/13d, 9d/13d, 11d/13d, 1};
+ double tol = 10E-12;
+ TestUtils.assertEquals(expectedBinUpperBounds, dist.getUpperBounds(), tol);
+ TestUtils.assertEquals(expectedGeneratorUpperBounds, dist.getGeneratorUpperBounds(), tol);
+ }
+
+ private void verifySame(EmpiricalDistribution d1, EmpiricalDistribution d2) {
+ assertEquals(d1.isLoaded(), d2.isLoaded());
+ assertEquals(d1.getBinCount(), d2.getBinCount());
+ assertEquals(d1.getSampleStats(), d2.getSampleStats());
+ if (d1.isLoaded()) {
+ for (int i = 0; i < d1.getUpperBounds().length; i++) {
+ assertEquals(d1.getUpperBounds()[i], d2.getUpperBounds()[i], 0);
+ }
+ assertEquals(d1.getBinStats(), d2.getBinStats());
+ }
+ }
+
+ private void tstGen(double tolerance)throws Exception {
+ empiricalDistribution.load(url);
+ SummaryStatistics stats = new SummaryStatistics();
+ for (int i = 1; i < 1000; i++) {
+ stats.addValue(empiricalDistribution.getNextValue());
+ }
+ assertEquals("mean", stats.getMean(),5.069831575018909,tolerance);
+ assertEquals
+ ("std dev", stats.getStandardDeviation(),1.0173699343977738,tolerance);
+ }
+
+ private void tstDoubleGen(double tolerance)throws Exception {
+ empiricalDistribution2.load(dataArray);
+ SummaryStatistics stats = new SummaryStatistics();
+ for (int i = 1; i < 1000; i++) {
+ stats.addValue(empiricalDistribution2.getNextValue());
+ }
+ assertEquals("mean", stats.getMean(),5.069831575018909,tolerance);
+ assertEquals
+ ("std dev", stats.getStandardDeviation(),1.0173699343977738,tolerance);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/random/GaussianRandomGeneratorTest.java b/src/test/java/org/apache/commons/math/random/GaussianRandomGeneratorTest.java
new file mode 100644
index 0000000..185d8b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/GaussianRandomGeneratorTest.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.random;
+
+import org.apache.commons.math.stat.StatUtils;
+
+import junit.framework.*;
+
+public class GaussianRandomGeneratorTest
+extends TestCase {
+
+ public GaussianRandomGeneratorTest(String name) {
+ super(name);
+ }
+
+ public void testMeanAndStandardDeviation() {
+ RandomGenerator rg = new JDKRandomGenerator();
+ rg.setSeed(17399225432l);
+ GaussianRandomGenerator generator = new GaussianRandomGenerator(rg);
+ double[] sample = new double[10000];
+ for (int i = 0; i < sample.length; ++i) {
+ sample[i] = generator.nextNormalizedDouble();
+ }
+ assertEquals(0.0, StatUtils.mean(sample), 0.012);
+ assertEquals(1.0, StatUtils.variance(sample), 0.01);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/MersenneTwisterTest.java b/src/test/java/org/apache/commons/math/random/MersenneTwisterTest.java
new file mode 100644
index 0000000..7547236
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/MersenneTwisterTest.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.random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class MersenneTwisterTest {
+
+ @Test
+ public void testGaussian() {
+ MersenneTwister mt = new MersenneTwister(42853252100l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 1000; ++i) {
+ sample.addValue(mt.nextGaussian());
+ }
+ assertEquals(0.0, sample.getMean(), 0.005);
+ assertEquals(1.0, sample.getStandardDeviation(), 0.025);
+ }
+
+ @Test
+ public void testDouble() {
+ MersenneTwister mt = new MersenneTwister(195357343514l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 1000; ++i) {
+ sample.addValue(mt.nextDouble());
+ }
+ assertEquals(0.5, sample.getMean(), 0.02);
+ assertEquals(1.0 / (2.0 * FastMath.sqrt(3.0)),
+ sample.getStandardDeviation(),
+ 0.002);
+ }
+
+ @Test
+ public void testFloat() {
+ MersenneTwister mt = new MersenneTwister(4442733263l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 1000; ++i) {
+ sample.addValue(mt.nextFloat());
+ }
+ assertEquals(0.5, sample.getMean(), 0.01);
+ assertEquals(1.0 / (2.0 * FastMath.sqrt(3.0)),
+ sample.getStandardDeviation(),
+ 0.006);
+ }
+
+ @Test(expected=java.lang.IllegalArgumentException.class)
+ public void testNextIntNeg() {
+ new MersenneTwister(1).nextInt(-1);
+ }
+
+ @Test
+ public void testNextIntN() {
+ MersenneTwister mt = new MersenneTwister(0x12b8a7412bb25el);
+ for (int n = 1; n < 20; ++n) {
+ int[] count = new int[n];
+ for (int k = 0; k < 10000; ++k) {
+ int l = mt.nextInt(n);
+ ++count[l];
+ assertTrue(l >= 0);
+ assertTrue(l < n);
+ }
+ for (int i = 0; i < n; ++i) {
+ assertTrue(n * count[i] > 8600);
+ assertTrue(n * count[i] < 11200);
+ }
+ }
+ }
+
+ @Test
+ public void testNextInt() {
+ MersenneTwister mt = new MersenneTwister(new int[] { 1, 2, 3, 4, 5 });
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextInt() >= 0) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ assertTrue(FastMath.abs(walk) < 120);
+ }
+
+ @Test
+ public void testNextLong() {
+ MersenneTwister mt = new MersenneTwister(12345);
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextLong() >= 0) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ assertTrue(FastMath.abs(walk) < 50);
+ }
+
+ @Test
+ public void testNexBoolean() {
+ MersenneTwister mt = new MersenneTwister(76342);
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextBoolean()) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ assertTrue(FastMath.abs(walk) < 250);
+ }
+
+ @Test
+ public void testNexBytes() {
+ MersenneTwister mt = new MersenneTwister(0);
+ int[] count = new int[256];
+ byte[] bytes = new byte[10];
+ for (int k = 0; k < 100000; ++k) {
+ mt.nextBytes(bytes);
+ for (byte b : bytes) {
+ ++count[b + 128];
+ }
+ }
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+ for (int c : count) {
+ min = FastMath.min(min, c);
+ max = FastMath.max(max, c);
+ }
+ int expected = (100000 * bytes.length) / count.length;
+ assertTrue((expected - 200) < min);
+ assertTrue(max < (expected + 200));
+ }
+
+ @Test
+ public void testMakotoNishimura() {
+ MersenneTwister mt = new MersenneTwister(new int[] {0x123, 0x234, 0x345, 0x456});
+ long[] refInt = {
+ 1067595299l, 955945823l, 477289528l, 4107218783l, 4228976476l, 3344332714l, 3355579695l, 227628506l,
+ 810200273l, 2591290167l, 2560260675l, 3242736208l, 646746669l, 1479517882l, 4245472273l, 1143372638l,
+ 3863670494l, 3221021970l, 1773610557l, 1138697238l, 1421897700l, 1269916527l, 2859934041l, 1764463362l,
+ 3874892047l, 3965319921l, 72549643l, 2383988930l, 2600218693l, 3237492380l, 2792901476l, 725331109l,
+ 605841842l, 271258942l, 715137098l, 3297999536l, 1322965544l, 4229579109l, 1395091102l, 3735697720l,
+ 2101727825l, 3730287744l, 2950434330l, 1661921839l, 2895579582l, 2370511479l, 1004092106l, 2247096681l,
+ 2111242379l, 3237345263l, 4082424759l, 219785033l, 2454039889l, 3709582971l, 835606218l, 2411949883l,
+ 2735205030l, 756421180l, 2175209704l, 1873865952l, 2762534237l, 4161807854l, 3351099340l, 181129879l,
+ 3269891896l, 776029799l, 2218161979l, 3001745796l, 1866825872l, 2133627728l, 34862734l, 1191934573l,
+ 3102311354l, 2916517763l, 1012402762l, 2184831317l, 4257399449l, 2899497138l, 3818095062l, 3030756734l,
+ 1282161629l, 420003642l, 2326421477l, 2741455717l, 1278020671l, 3744179621l, 271777016l, 2626330018l,
+ 2560563991l, 3055977700l, 4233527566l, 1228397661l, 3595579322l, 1077915006l, 2395931898l, 1851927286l,
+ 3013683506l, 1999971931l, 3006888962l, 1049781534l, 1488758959l, 3491776230l, 104418065l, 2448267297l,
+ 3075614115l, 3872332600l, 891912190l, 3936547759l, 2269180963l, 2633455084l, 1047636807l, 2604612377l,
+ 2709305729l, 1952216715l, 207593580l, 2849898034l, 670771757l, 2210471108l, 467711165l, 263046873l,
+ 3569667915l, 1042291111l, 3863517079l, 1464270005l, 2758321352l, 3790799816l, 2301278724l, 3106281430l,
+ 7974801l, 2792461636l, 555991332l, 621766759l, 1322453093l, 853629228l, 686962251l, 1455120532l,
+ 957753161l, 1802033300l, 1021534190l, 3486047311l, 1902128914l, 3701138056l, 4176424663l, 1795608698l,
+ 560858864l, 3737752754l, 3141170998l, 1553553385l, 3367807274l, 711546358l, 2475125503l, 262969859l,
+ 251416325l, 2980076994l, 1806565895l, 969527843l, 3529327173l, 2736343040l, 2987196734l, 1649016367l,
+ 2206175811l, 3048174801l, 3662503553l, 3138851612l, 2660143804l, 1663017612l, 1816683231l, 411916003l,
+ 3887461314l, 2347044079l, 1015311755l, 1203592432l, 2170947766l, 2569420716l, 813872093l, 1105387678l,
+ 1431142475l, 220570551l, 4243632715l, 4179591855l, 2607469131l, 3090613241l, 282341803l, 1734241730l,
+ 1391822177l, 1001254810l, 827927915l, 1886687171l, 3935097347l, 2631788714l, 3905163266l, 110554195l,
+ 2447955646l, 3717202975l, 3304793075l, 3739614479l, 3059127468l, 953919171l, 2590123714l, 1132511021l,
+ 3795593679l, 2788030429l, 982155079l, 3472349556l, 859942552l, 2681007391l, 2299624053l, 647443547l,
+ 233600422l, 608168955l, 3689327453l, 1849778220l, 1608438222l, 3968158357l, 2692977776l, 2851872572l,
+ 246750393l, 3582818628l, 3329652309l, 4036366910l, 1012970930l, 950780808l, 3959768744l, 2538550045l,
+ 191422718l, 2658142375l, 3276369011l, 2927737484l, 1234200027l, 1920815603l, 3536074689l, 1535612501l,
+ 2184142071l, 3276955054l, 428488088l, 2378411984l, 4059769550l, 3913744741l, 2732139246l, 64369859l,
+ 3755670074l, 842839565l, 2819894466l, 2414718973l, 1010060670l, 1839715346l, 2410311136l, 152774329l,
+ 3485009480l, 4102101512l, 2852724304l, 879944024l, 1785007662l, 2748284463l, 1354768064l, 3267784736l,
+ 2269127717l, 3001240761l, 3179796763l, 895723219l, 865924942l, 4291570937l, 89355264l, 1471026971l,
+ 4114180745l, 3201939751l, 2867476999l, 2460866060l, 3603874571l, 2238880432l, 3308416168l, 2072246611l,
+ 2755653839l, 3773737248l, 1709066580l, 4282731467l, 2746170170l, 2832568330l, 433439009l, 3175778732l,
+ 26248366l, 2551382801l, 183214346l, 3893339516l, 1928168445l, 1337157619l, 3429096554l, 3275170900l,
+ 1782047316l, 4264403756l, 1876594403l, 4289659572l, 3223834894l, 1728705513l, 4068244734l, 2867840287l,
+ 1147798696l, 302879820l, 1730407747l, 1923824407l, 1180597908l, 1569786639l, 198796327l, 560793173l,
+ 2107345620l, 2705990316l, 3448772106l, 3678374155l, 758635715l, 884524671l, 486356516l, 1774865603l,
+ 3881226226l, 2635213607l, 1181121587l, 1508809820l, 3178988241l, 1594193633l, 1235154121l, 326117244l,
+ 2304031425l, 937054774l, 2687415945l, 3192389340l, 2003740439l, 1823766188l, 2759543402l, 10067710l,
+ 1533252662l, 4132494984l, 82378136l, 420615890l, 3467563163l, 541562091l, 3535949864l, 2277319197l,
+ 3330822853l, 3215654174l, 4113831979l, 4204996991l, 2162248333l, 3255093522l, 2219088909l, 2978279037l,
+ 255818579l, 2859348628l, 3097280311l, 2569721123l, 1861951120l, 2907080079l, 2719467166l, 998319094l,
+ 2521935127l, 2404125338l, 259456032l, 2086860995l, 1839848496l, 1893547357l, 2527997525l, 1489393124l,
+ 2860855349l, 76448234l, 2264934035l, 744914583l, 2586791259l, 1385380501l, 66529922l, 1819103258l,
+ 1899300332l, 2098173828l, 1793831094l, 276463159l, 360132945l, 4178212058l, 595015228l, 177071838l,
+ 2800080290l, 1573557746l, 1548998935l, 378454223l, 1460534296l, 1116274283l, 3112385063l, 3709761796l,
+ 827999348l, 3580042847l, 1913901014l, 614021289l, 4278528023l, 1905177404l, 45407939l, 3298183234l,
+ 1184848810l, 3644926330l, 3923635459l, 1627046213l, 3677876759l, 969772772l, 1160524753l, 1522441192l,
+ 452369933l, 1527502551l, 832490847l, 1003299676l, 1071381111l, 2891255476l, 973747308l, 4086897108l,
+ 1847554542l, 3895651598l, 2227820339l, 1621250941l, 2881344691l, 3583565821l, 3510404498l, 849362119l,
+ 862871471l, 797858058l, 2867774932l, 2821282612l, 3272403146l, 3997979905l, 209178708l, 1805135652l,
+ 6783381l, 2823361423l, 792580494l, 4263749770l, 776439581l, 3798193823l, 2853444094l, 2729507474l,
+ 1071873341l, 1329010206l, 1289336450l, 3327680758l, 2011491779l, 80157208l, 922428856l, 1158943220l,
+ 1667230961l, 2461022820l, 2608845159l, 387516115l, 3345351910l, 1495629111l, 4098154157l, 3156649613l,
+ 3525698599l, 4134908037l, 446713264l, 2137537399l, 3617403512l, 813966752l, 1157943946l, 3734692965l,
+ 1680301658l, 3180398473l, 3509854711l, 2228114612l, 1008102291l, 486805123l, 863791847l, 3189125290l,
+ 1050308116l, 3777341526l, 4291726501l, 844061465l, 1347461791l, 2826481581l, 745465012l, 2055805750l,
+ 4260209475l, 2386693097l, 2980646741l, 447229436l, 2077782664l, 1232942813l, 4023002732l, 1399011509l,
+ 3140569849l, 2579909222l, 3794857471l, 900758066l, 2887199683l, 1720257997l, 3367494931l, 2668921229l,
+ 955539029l, 3818726432l, 1105704962l, 3889207255l, 2277369307l, 2746484505l, 1761846513l, 2413916784l,
+ 2685127085l, 4240257943l, 1166726899l, 4215215715l, 3082092067l, 3960461946l, 1663304043l, 2087473241l,
+ 4162589986l, 2507310778l, 1579665506l, 767234210l, 970676017l, 492207530l, 1441679602l, 1314785090l,
+ 3262202570l, 3417091742l, 1561989210l, 3011406780l, 1146609202l, 3262321040l, 1374872171l, 1634688712l,
+ 1280458888l, 2230023982l, 419323804l, 3262899800l, 39783310l, 1641619040l, 1700368658l, 2207946628l,
+ 2571300939l, 2424079766l, 780290914l, 2715195096l, 3390957695l, 163151474l, 2309534542l, 1860018424l,
+ 555755123l, 280320104l, 1604831083l, 2713022383l, 1728987441l, 3639955502l, 623065489l, 3828630947l,
+ 4275479050l, 3516347383l, 2343951195l, 2430677756l, 635534992l, 3868699749l, 808442435l, 3070644069l,
+ 4282166003l, 2093181383l, 2023555632l, 1568662086l, 3422372620l, 4134522350l, 3016979543l, 3259320234l,
+ 2888030729l, 3185253876l, 4258779643l, 1267304371l, 1022517473l, 815943045l, 929020012l, 2995251018l,
+ 3371283296l, 3608029049l, 2018485115l, 122123397l, 2810669150l, 1411365618l, 1238391329l, 1186786476l,
+ 3155969091l, 2242941310l, 1765554882l, 279121160l, 4279838515l, 1641578514l, 3796324015l, 13351065l,
+ 103516986l, 1609694427l, 551411743l, 2493771609l, 1316337047l, 3932650856l, 4189700203l, 463397996l,
+ 2937735066l, 1855616529l, 2626847990l, 55091862l, 3823351211l, 753448970l, 4045045500l, 1274127772l,
+ 1124182256l, 92039808l, 2126345552l, 425973257l, 386287896l, 2589870191l, 1987762798l, 4084826973l,
+ 2172456685l, 3366583455l, 3602966653l, 2378803535l, 2901764433l, 3716929006l, 3710159000l, 2653449155l,
+ 3469742630l, 3096444476l, 3932564653l, 2595257433l, 318974657l, 3146202484l, 853571438l, 144400272l,
+ 3768408841l, 782634401l, 2161109003l, 570039522l, 1886241521l, 14249488l, 2230804228l, 1604941699l,
+ 3928713335l, 3921942509l, 2155806892l, 134366254l, 430507376l, 1924011722l, 276713377l, 196481886l,
+ 3614810992l, 1610021185l, 1785757066l, 851346168l, 3761148643l, 2918835642l, 3364422385l, 3012284466l,
+ 3735958851l, 2643153892l, 3778608231l, 1164289832l, 205853021l, 2876112231l, 3503398282l, 3078397001l,
+ 3472037921l, 1748894853l, 2740861475l, 316056182l, 1660426908l, 168885906l, 956005527l, 3984354789l,
+ 566521563l, 1001109523l, 1216710575l, 2952284757l, 3834433081l, 3842608301l, 2467352408l, 3974441264l,
+ 3256601745l, 1409353924l, 1329904859l, 2307560293l, 3125217879l, 3622920184l, 3832785684l, 3882365951l,
+ 2308537115l, 2659155028l, 1450441945l, 3532257603l, 3186324194l, 1225603425l, 1124246549l, 175808705l,
+ 3009142319l, 2796710159l, 3651990107l, 160762750l, 1902254979l, 1698648476l, 1134980669l, 497144426l,
+ 3302689335l, 4057485630l, 3603530763l, 4087252587l, 427812652l, 286876201l, 823134128l, 1627554964l,
+ 3745564327l, 2589226092l, 4202024494l, 62878473l, 3275585894l, 3987124064l, 2791777159l, 1916869511l,
+ 2585861905l, 1375038919l, 1403421920l, 60249114l, 3811870450l, 3021498009l, 2612993202l, 528933105l,
+ 2757361321l, 3341402964l, 2621861700l, 273128190l, 4015252178l, 3094781002l, 1621621288l, 2337611177l,
+ 1796718448l, 1258965619l, 4241913140l, 2138560392l, 3022190223l, 4174180924l, 450094611l, 3274724580l,
+ 617150026l, 2704660665l, 1469700689l, 1341616587l, 356715071l, 1188789960l, 2278869135l, 1766569160l,
+ 2795896635l, 57824704l, 2893496380l, 1235723989l, 1630694347l, 3927960522l, 428891364l, 1814070806l,
+ 2287999787l, 4125941184l, 3968103889l, 3548724050l, 1025597707l, 1404281500l, 2002212197l, 92429143l,
+ 2313943944l, 2403086080l, 3006180634l, 3561981764l, 1671860914l, 1768520622l, 1803542985l, 844848113l,
+ 3006139921l, 1410888995l, 1157749833l, 2125704913l, 1789979528l, 1799263423l, 741157179l, 2405862309l,
+ 767040434l, 2655241390l, 3663420179l, 2172009096l, 2511931187l, 1680542666l, 231857466l, 1154981000l,
+ 157168255l, 1454112128l, 3505872099l, 1929775046l, 2309422350l, 2143329496l, 2960716902l, 407610648l,
+ 2938108129l, 2581749599l, 538837155l, 2342628867l, 430543915l, 740188568l, 1937713272l, 3315215132l,
+ 2085587024l, 4030765687l, 766054429l, 3517641839l, 689721775l, 1294158986l, 1753287754l, 4202601348l,
+ 1974852792l, 33459103l, 3568087535l, 3144677435l, 1686130825l, 4134943013l, 3005738435l, 3599293386l,
+ 426570142l, 754104406l, 3660892564l, 1964545167l, 829466833l, 821587464l, 1746693036l, 1006492428l,
+ 1595312919l, 1256599985l, 1024482560l, 1897312280l, 2902903201l, 691790057l, 1037515867l, 3176831208l,
+ 1968401055l, 2173506824l, 1089055278l, 1748401123l, 2941380082l, 968412354l, 1818753861l, 2973200866l,
+ 3875951774l, 1119354008l, 3988604139l, 1647155589l, 2232450826l, 3486058011l, 3655784043l, 3759258462l,
+ 847163678l, 1082052057l, 989516446l, 2871541755l, 3196311070l, 3929963078l, 658187585l, 3664944641l,
+ 2175149170l, 2203709147l, 2756014689l, 2456473919l, 3890267390l, 1293787864l, 2830347984l, 3059280931l,
+ 4158802520l, 1561677400l, 2586570938l, 783570352l, 1355506163l, 31495586l, 3789437343l, 3340549429l,
+ 2092501630l, 896419368l, 671715824l, 3530450081l, 3603554138l, 1055991716l, 3442308219l, 1499434728l,
+ 3130288473l, 3639507000l, 17769680l, 2259741420l, 487032199l, 4227143402l, 3693771256l, 1880482820l,
+ 3924810796l, 381462353l, 4017855991l, 2452034943l, 2736680833l, 2209866385l, 2128986379l, 437874044l,
+ 595759426l, 641721026l, 1636065708l, 3899136933l, 629879088l, 3591174506l, 351984326l, 2638783544l,
+ 2348444281l, 2341604660l, 2123933692l, 143443325l, 1525942256l, 364660499l, 599149312l, 939093251l,
+ 1523003209l, 106601097l, 376589484l, 1346282236l, 1297387043l, 764598052l, 3741218111l, 933457002l,
+ 1886424424l, 3219631016l, 525405256l, 3014235619l, 323149677l, 2038881721l, 4100129043l, 2851715101l,
+ 2984028078l, 1888574695l, 2014194741l, 3515193880l, 4180573530l, 3461824363l, 2641995497l, 3179230245l,
+ 2902294983l, 2217320456l, 4040852155l, 1784656905l, 3311906931l, 87498458l, 2752971818l, 2635474297l,
+ 2831215366l, 3682231106l, 2920043893l, 3772929704l, 2816374944l, 309949752l, 2383758854l, 154870719l,
+ 385111597l, 1191604312l, 1840700563l, 872191186l, 2925548701l, 1310412747l, 2102066999l, 1504727249l,
+ 3574298750l, 1191230036l, 3330575266l, 3180292097l, 3539347721l, 681369118l, 3305125752l, 3648233597l,
+ 950049240l, 4173257693l, 1760124957l, 512151405l, 681175196l, 580563018l, 1169662867l, 4015033554l,
+ 2687781101l, 699691603l, 2673494188l, 1137221356l, 123599888l, 472658308l, 1053598179l, 1012713758l,
+ 3481064843l, 3759461013l, 3981457956l, 3830587662l, 1877191791l, 3650996736l, 988064871l, 3515461600l,
+ 4089077232l, 2225147448l, 1249609188l, 2643151863l, 3896204135l, 2416995901l, 1397735321l, 3460025646l
+ };
+ double[] refDouble = {
+ 0.76275443, 0.99000644, 0.98670464, 0.10143112, 0.27933125, 0.69867227, 0.94218740, 0.03427201,
+ 0.78842173, 0.28180608, 0.92179002, 0.20785655, 0.54534773, 0.69644020, 0.38107718, 0.23978165,
+ 0.65286910, 0.07514568, 0.22765211, 0.94872929, 0.74557914, 0.62664415, 0.54708246, 0.90959343,
+ 0.42043116, 0.86334511, 0.19189126, 0.14718544, 0.70259889, 0.63426346, 0.77408121, 0.04531601,
+ 0.04605807, 0.88595519, 0.69398270, 0.05377184, 0.61711170, 0.05565708, 0.10133577, 0.41500776,
+ 0.91810699, 0.22320679, 0.23353705, 0.92871862, 0.98897234, 0.19786706, 0.80558809, 0.06961067,
+ 0.55840445, 0.90479405, 0.63288060, 0.95009721, 0.54948447, 0.20645042, 0.45000959, 0.87050869,
+ 0.70806991, 0.19406895, 0.79286390, 0.49332866, 0.78483914, 0.75145146, 0.12341941, 0.42030252,
+ 0.16728160, 0.59906494, 0.37575460, 0.97815160, 0.39815952, 0.43595080, 0.04952478, 0.33917805,
+ 0.76509902, 0.61034321, 0.90654701, 0.92915732, 0.85365931, 0.18812377, 0.65913428, 0.28814566,
+ 0.59476081, 0.27835931, 0.60722542, 0.68310435, 0.69387186, 0.03699800, 0.65897714, 0.17527003,
+ 0.02889304, 0.86777366, 0.12352068, 0.91439461, 0.32022990, 0.44445731, 0.34903686, 0.74639273,
+ 0.65918367, 0.92492794, 0.31872642, 0.77749724, 0.85413832, 0.76385624, 0.32744211, 0.91326300,
+ 0.27458185, 0.22190155, 0.19865383, 0.31227402, 0.85321225, 0.84243342, 0.78544200, 0.71854080,
+ 0.92503892, 0.82703064, 0.88306297, 0.47284073, 0.70059042, 0.48003761, 0.38671694, 0.60465770,
+ 0.41747204, 0.47163243, 0.72750808, 0.65830223, 0.10955369, 0.64215401, 0.23456345, 0.95944940,
+ 0.72822249, 0.40888451, 0.69980355, 0.26677428, 0.57333635, 0.39791582, 0.85377858, 0.76962816,
+ 0.72004885, 0.90903087, 0.51376506, 0.37732665, 0.12691640, 0.71249738, 0.81217908, 0.37037313,
+ 0.32772374, 0.14238259, 0.05614811, 0.74363008, 0.39773267, 0.94859135, 0.31452454, 0.11730313,
+ 0.62962618, 0.33334237, 0.45547255, 0.10089665, 0.56550662, 0.60539371, 0.16027624, 0.13245301,
+ 0.60959939, 0.04671662, 0.99356286, 0.57660859, 0.40269560, 0.45274629, 0.06699735, 0.85064246,
+ 0.87742744, 0.54508392, 0.87242982, 0.29321385, 0.67660627, 0.68230715, 0.79052073, 0.48592054,
+ 0.25186266, 0.93769755, 0.28565487, 0.47219067, 0.99054882, 0.13155240, 0.47110470, 0.98556600,
+ 0.84397623, 0.12875246, 0.90953202, 0.49129015, 0.23792727, 0.79481194, 0.44337770, 0.96564297,
+ 0.67749118, 0.55684872, 0.27286897, 0.79538393, 0.61965356, 0.22487929, 0.02226018, 0.49248200,
+ 0.42247006, 0.91797788, 0.99250134, 0.23449967, 0.52531508, 0.10246337, 0.78685622, 0.34310922,
+ 0.89892996, 0.40454552, 0.68608407, 0.30752487, 0.83601319, 0.54956031, 0.63777550, 0.82199797,
+ 0.24890696, 0.48801123, 0.48661910, 0.51223987, 0.32969635, 0.31075073, 0.21393155, 0.73453207,
+ 0.15565705, 0.58584522, 0.28976728, 0.97621478, 0.61498701, 0.23891470, 0.28518540, 0.46809591,
+ 0.18371914, 0.37597910, 0.13492176, 0.66849449, 0.82811466, 0.56240330, 0.37548956, 0.27562998,
+ 0.27521910, 0.74096121, 0.77176757, 0.13748143, 0.99747138, 0.92504502, 0.09175241, 0.21389176,
+ 0.21766512, 0.31183245, 0.23271221, 0.21207367, 0.57903312, 0.77523344, 0.13242613, 0.31037988,
+ 0.01204835, 0.71652949, 0.84487594, 0.14982178, 0.57423142, 0.45677888, 0.48420169, 0.53465428,
+ 0.52667473, 0.46880526, 0.49849733, 0.05670710, 0.79022476, 0.03872047, 0.21697212, 0.20443086,
+ 0.28949326, 0.81678186, 0.87629474, 0.92297064, 0.27373097, 0.84625273, 0.51505586, 0.00582792,
+ 0.33295971, 0.91848412, 0.92537226, 0.91760033, 0.07541125, 0.71745848, 0.61158698, 0.00941650,
+ 0.03135554, 0.71527471, 0.24821915, 0.63636652, 0.86159918, 0.26450229, 0.60160194, 0.35557725,
+ 0.24477500, 0.07186456, 0.51757096, 0.62120362, 0.97981062, 0.69954667, 0.21065616, 0.13382753,
+ 0.27693186, 0.59644095, 0.71500764, 0.04110751, 0.95730081, 0.91600724, 0.47704678, 0.26183479,
+ 0.34706971, 0.07545431, 0.29398385, 0.93236070, 0.60486023, 0.48015011, 0.08870451, 0.45548581,
+ 0.91872718, 0.38142712, 0.10668643, 0.01397541, 0.04520355, 0.93822273, 0.18011940, 0.57577277,
+ 0.91427606, 0.30911399, 0.95853475, 0.23611214, 0.69619891, 0.69601980, 0.76765372, 0.58515930,
+ 0.49479057, 0.11288752, 0.97187699, 0.32095365, 0.57563608, 0.40760618, 0.78703383, 0.43261152,
+ 0.90877651, 0.84686346, 0.10599030, 0.72872803, 0.19315490, 0.66152912, 0.10210518, 0.06257876,
+ 0.47950688, 0.47062066, 0.72701157, 0.48915116, 0.66110261, 0.60170685, 0.24516994, 0.12726050,
+ 0.03451185, 0.90864994, 0.83494878, 0.94800035, 0.91035206, 0.14480751, 0.88458997, 0.53498312,
+ 0.15963215, 0.55378627, 0.35171349, 0.28719791, 0.09097957, 0.00667896, 0.32309622, 0.87561479,
+ 0.42534520, 0.91748977, 0.73908457, 0.41793223, 0.99279792, 0.87908370, 0.28458072, 0.59132853,
+ 0.98672190, 0.28547393, 0.09452165, 0.89910674, 0.53681109, 0.37931425, 0.62683489, 0.56609740,
+ 0.24801549, 0.52948179, 0.98328855, 0.66403523, 0.55523786, 0.75886666, 0.84784685, 0.86829981,
+ 0.71448906, 0.84670080, 0.43922919, 0.20771016, 0.64157936, 0.25664246, 0.73055695, 0.86395782,
+ 0.65852932, 0.99061803, 0.40280575, 0.39146298, 0.07291005, 0.97200603, 0.20555729, 0.59616495,
+ 0.08138254, 0.45796388, 0.33681125, 0.33989127, 0.18717090, 0.53545811, 0.60550838, 0.86520709,
+ 0.34290701, 0.72743276, 0.73023855, 0.34195926, 0.65019733, 0.02765254, 0.72575740, 0.32709576,
+ 0.03420866, 0.26061893, 0.56997511, 0.28439072, 0.84422744, 0.77637570, 0.55982168, 0.06720327,
+ 0.58449067, 0.71657369, 0.15819609, 0.58042821, 0.07947911, 0.40193792, 0.11376012, 0.88762938,
+ 0.67532159, 0.71223735, 0.27829114, 0.04806073, 0.21144026, 0.58830274, 0.04140071, 0.43215628,
+ 0.12952729, 0.94668759, 0.87391019, 0.98382450, 0.27750768, 0.90849647, 0.90962737, 0.59269720,
+ 0.96102026, 0.49544979, 0.32007095, 0.62585546, 0.03119821, 0.85953001, 0.22017528, 0.05834068,
+ 0.80731217, 0.53799961, 0.74166948, 0.77426600, 0.43938444, 0.54862081, 0.58575513, 0.15886492,
+ 0.73214332, 0.11649057, 0.77463977, 0.85788827, 0.17061997, 0.66838056, 0.96076133, 0.07949296,
+ 0.68521946, 0.89986254, 0.05667410, 0.12741385, 0.83470977, 0.63969104, 0.46612929, 0.10200126,
+ 0.01194925, 0.10476340, 0.90285217, 0.31221221, 0.32980614, 0.46041971, 0.52024973, 0.05425470,
+ 0.28330912, 0.60426543, 0.00598243, 0.97244013, 0.21135841, 0.78561597, 0.78428734, 0.63422849,
+ 0.32909934, 0.44771136, 0.27380750, 0.14966697, 0.18156268, 0.65686758, 0.28726350, 0.97074787,
+ 0.63676171, 0.96649494, 0.24526295, 0.08297372, 0.54257548, 0.03166785, 0.33735355, 0.15946671,
+ 0.02102971, 0.46228045, 0.11892296, 0.33408336, 0.29875681, 0.29847692, 0.73767569, 0.02080745,
+ 0.62980060, 0.08082293, 0.22993106, 0.25031439, 0.87787525, 0.45150053, 0.13673441, 0.63407612,
+ 0.97907688, 0.52241942, 0.50580158, 0.06273902, 0.05270283, 0.77031811, 0.05113352, 0.24393329,
+ 0.75036441, 0.37436336, 0.22877652, 0.59975358, 0.85707591, 0.88691457, 0.85547165, 0.36641027,
+ 0.58720133, 0.45462835, 0.09243817, 0.32981586, 0.07820411, 0.25421519, 0.36004706, 0.60092307,
+ 0.46192412, 0.36758683, 0.98424170, 0.08019934, 0.68594024, 0.45826386, 0.29962317, 0.79365413,
+ 0.89231296, 0.49478547, 0.87645944, 0.23590734, 0.28106737, 0.75026285, 0.08136314, 0.79582424,
+ 0.76010628, 0.82792971, 0.27947652, 0.72482861, 0.82191216, 0.46171689, 0.79189752, 0.96043686,
+ 0.51609668, 0.88995725, 0.28998963, 0.55191845, 0.03934737, 0.83033700, 0.49553013, 0.98009549,
+ 0.19017594, 0.98347750, 0.33452066, 0.87144372, 0.72106301, 0.71272114, 0.71465963, 0.88361677,
+ 0.85571283, 0.73782329, 0.20920458, 0.34855153, 0.46766817, 0.02780062, 0.74898344, 0.03680650,
+ 0.44866557, 0.77426312, 0.91025891, 0.25195236, 0.87319953, 0.63265037, 0.25552148, 0.27422476,
+ 0.95217406, 0.39281839, 0.66441573, 0.09158900, 0.94515992, 0.07800798, 0.02507888, 0.39901462,
+ 0.17382573, 0.12141278, 0.85502334, 0.19902911, 0.02160210, 0.44460522, 0.14688742, 0.68020336,
+ 0.71323733, 0.60922473, 0.95400380, 0.99611159, 0.90897777, 0.41073520, 0.66206647, 0.32064685,
+ 0.62805003, 0.50677209, 0.52690101, 0.87473387, 0.73918362, 0.39826974, 0.43683919, 0.80459118,
+ 0.32422684, 0.01958019, 0.95319576, 0.98326137, 0.83931735, 0.69060863, 0.33671416, 0.68062550,
+ 0.65152380, 0.33392969, 0.03451730, 0.95227244, 0.68200635, 0.85074171, 0.64721009, 0.51234433,
+ 0.73402047, 0.00969637, 0.93835057, 0.80803854, 0.31485260, 0.20089527, 0.01323282, 0.59933780,
+ 0.31584602, 0.20209563, 0.33754800, 0.68604181, 0.24443049, 0.19952227, 0.78162632, 0.10336988,
+ 0.11360736, 0.23536740, 0.23262256, 0.67803776, 0.48749791, 0.74658435, 0.92156640, 0.56706407,
+ 0.36683221, 0.99157136, 0.23421374, 0.45183767, 0.91609720, 0.85573315, 0.37706276, 0.77042618,
+ 0.30891908, 0.40709595, 0.06944866, 0.61342849, 0.88817388, 0.58734506, 0.98711323, 0.14744128,
+ 0.63242656, 0.87704136, 0.68347125, 0.84446569, 0.43265239, 0.25146321, 0.04130111, 0.34259839,
+ 0.92697368, 0.40878778, 0.56990338, 0.76204273, 0.19820348, 0.66314909, 0.02482844, 0.06669207,
+ 0.50205581, 0.26084093, 0.65139159, 0.41650223, 0.09733904, 0.56344203, 0.62651696, 0.67332139,
+ 0.58037374, 0.47258086, 0.21010758, 0.05713135, 0.89390629, 0.10781246, 0.32037450, 0.07628388,
+ 0.34227964, 0.42190597, 0.58201860, 0.77363549, 0.49595133, 0.86031236, 0.83906769, 0.81098161,
+ 0.26694195, 0.14215941, 0.88210306, 0.53634237, 0.12090720, 0.82480459, 0.75930318, 0.31847147,
+ 0.92768077, 0.01037616, 0.56201727, 0.88107122, 0.35925856, 0.85860762, 0.61109408, 0.70408301,
+ 0.58434977, 0.92192494, 0.62667915, 0.75988365, 0.06858761, 0.36156496, 0.58057195, 0.13636150,
+ 0.57719713, 0.59340255, 0.63530602, 0.22976282, 0.71915530, 0.41162531, 0.63979565, 0.09931342,
+ 0.79344045, 0.10893790, 0.84450224, 0.23122236, 0.99485593, 0.73637397, 0.17276368, 0.13357764,
+ 0.74965804, 0.64991737, 0.61990341, 0.41523170, 0.05878239, 0.05687301, 0.05497131, 0.42868366,
+ 0.42571090, 0.25810502, 0.89642955, 0.30439758, 0.39310223, 0.11357431, 0.04288255, 0.23397550,
+ 0.11200634, 0.85621396, 0.89733974, 0.37508865, 0.42077265, 0.68597384, 0.72781399, 0.19296476,
+ 0.61699087, 0.31667128, 0.67756410, 0.00177323, 0.05725176, 0.79474693, 0.18885238, 0.06724856,
+ 0.68193156, 0.42202167, 0.22082041, 0.28554673, 0.64995708, 0.87851940, 0.29124547, 0.61009521,
+ 0.87374537, 0.05743712, 0.69902994, 0.81925115, 0.45653873, 0.37236821, 0.31118709, 0.52734307,
+ 0.39672836, 0.38185294, 0.30163915, 0.17374510, 0.04913278, 0.90404879, 0.25742801, 0.58266467,
+ 0.97663209, 0.79823377, 0.36437958, 0.15206043, 0.26529938, 0.22690047, 0.05839021, 0.84721160,
+ 0.18622435, 0.37809403, 0.55706977, 0.49828704, 0.47659049, 0.24289680, 0.88477595, 0.07807463,
+ 0.56245739, 0.73490635, 0.21099431, 0.13164942, 0.75840044, 0.66877037, 0.28988183, 0.44046090,
+ 0.24967434, 0.80048356, 0.26029740, 0.30416821, 0.64151867, 0.52067892, 0.12880774, 0.85465381,
+ 0.02690525, 0.19149288, 0.49630295, 0.79682619, 0.43566145, 0.00288078, 0.81484193, 0.03763639,
+ 0.68529083, 0.01339574, 0.38405386, 0.30537067, 0.22994703, 0.44000045, 0.27217985, 0.53831243,
+ 0.02870435, 0.86282045, 0.61831306, 0.09164956, 0.25609707, 0.07445781, 0.72185784, 0.90058883,
+ 0.30070608, 0.94476583, 0.56822213, 0.21933909, 0.96772793, 0.80063440, 0.26307906, 0.31183306,
+ 0.16501252, 0.55436179, 0.68562285, 0.23829083, 0.86511559, 0.57868991, 0.81888344, 0.20126869,
+ 0.93172350, 0.66028129, 0.21786948, 0.78515828, 0.10262106, 0.35390326, 0.79303876, 0.63427924,
+ 0.90479631, 0.31024934, 0.60635447, 0.56198079, 0.63573813, 0.91854197, 0.99701497, 0.83085849,
+ 0.31692291, 0.01925964, 0.97446405, 0.98751283, 0.60944293, 0.13751018, 0.69519957, 0.68956636,
+ 0.56969015, 0.46440193, 0.88341765, 0.36754434, 0.89223647, 0.39786427, 0.85055280, 0.12749961,
+ 0.79452122, 0.89449784, 0.14567830, 0.45716830, 0.74822309, 0.28200437, 0.42546044, 0.17464886,
+ 0.68308746, 0.65496587, 0.52935411, 0.12736159, 0.61523955, 0.81590528, 0.63107864, 0.39786553,
+ 0.20102294, 0.53292914, 0.75485590, 0.59847044, 0.32861691, 0.12125866, 0.58917183, 0.07638293,
+ 0.86845380, 0.29192617, 0.03989733, 0.52180460, 0.32503407, 0.64071852, 0.69516575, 0.74254998,
+ 0.54587026, 0.48713246, 0.32920155, 0.08719954, 0.63497059, 0.54328459, 0.64178757, 0.45583809,
+ 0.70694291, 0.85212760, 0.86074305, 0.33163422, 0.85739792, 0.59908488, 0.74566046, 0.72157152
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ int r = mt.nextInt();
+ assertEquals(refInt[i], (r & 0x7fffffffl) | ((r < 0) ? 0x80000000l : 0x0l));
+ }
+
+ for (int i = 0; i < refDouble.length; ++i) {
+ int r = mt.nextInt();
+ assertEquals(refDouble[i],
+ ((r & 0x7fffffffl) | ((r < 0) ? 0x80000000l : 0x0l)) / 4294967296.0,
+ 1.0e-8);
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/RandomAdaptorTest.java b/src/test/java/org/apache/commons/math/random/RandomAdaptorTest.java
new file mode 100644
index 0000000..d4e9305
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/RandomAdaptorTest.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.random;
+
+import java.util.Random;
+
+/**
+ * Test cases for the RandomAdaptor class
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class RandomAdaptorTest extends RandomDataTest {
+
+ public RandomAdaptorTest(String name) {
+ super(name);
+ }
+
+ public void testAdaptor() {
+ ConstantGenerator generator = new ConstantGenerator();
+ Random random = RandomAdaptor.createAdaptor(generator);
+ checkConstant(random);
+ RandomAdaptor randomAdaptor = new RandomAdaptor(generator);
+ checkConstant(randomAdaptor);
+ }
+
+ private void checkConstant(Random random) {
+ byte[] bytes = new byte[] {0};
+ random.nextBytes(bytes);
+ assertEquals(0, bytes[0]);
+ assertEquals(false, random.nextBoolean());
+ assertEquals(0, random.nextDouble(), 0);
+ assertEquals(0, random.nextFloat(), 0);
+ assertEquals(0, random.nextGaussian(), 0);
+ assertEquals(0, random.nextInt());
+ assertEquals(0, random.nextInt(1));
+ assertEquals(0, random.nextLong());
+ random.setSeed(100);
+ assertEquals(0, random.nextDouble(), 0);
+ }
+
+ /*
+ * "Constant" generator to test Adaptor delegation.
+ * "Powered by Eclipse ;-)"
+ *
+ */
+ private static class ConstantGenerator implements RandomGenerator {
+
+ public boolean nextBoolean() {
+ return false;
+ }
+
+ public void nextBytes(byte[] bytes) {
+ }
+
+ public double nextDouble() {
+ return 0;
+ }
+
+ public float nextFloat() {
+ return 0;
+ }
+
+ public double nextGaussian() {
+ return 0;
+ }
+
+ public int nextInt() {
+ return 0;
+ }
+
+ public int nextInt(int n) {
+ return 0;
+ }
+
+ public long nextLong() {
+ return 0;
+ }
+
+ public void setSeed(int seed) {
+ }
+
+ public void setSeed(int[] seed) {
+ }
+
+ public void setSeed(long seed) {
+ }
+
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/random/RandomDataTest.java b/src/test/java/org/apache/commons/math/random/RandomDataTest.java
new file mode 100644
index 0000000..d117021
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/RandomDataTest.java
@@ -0,0 +1,990 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.commons.math.RetryTestCase;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.distribution.BetaDistributionImpl;
+import org.apache.commons.math.distribution.BinomialDistributionImpl;
+import org.apache.commons.math.distribution.BinomialDistributionTest;
+import org.apache.commons.math.distribution.CauchyDistributionImpl;
+import org.apache.commons.math.distribution.ChiSquaredDistributionImpl;
+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.HypergeometricDistributionTest;
+import org.apache.commons.math.distribution.PascalDistributionImpl;
+import org.apache.commons.math.distribution.PascalDistributionTest;
+import org.apache.commons.math.distribution.PoissonDistribution;
+import org.apache.commons.math.distribution.PoissonDistributionImpl;
+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.distribution.ZipfDistributionTest;
+import org.apache.commons.math.stat.Frequency;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math.stat.inference.ChiSquareTest;
+import org.apache.commons.math.stat.inference.ChiSquareTestImpl;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the RandomData class.
+ *
+ * @version $Revision: 1042376 $ $Date: 2009-04-05 11:55:59 -0500 (Sun, 05 Apr
+ * 2009) $
+ */
+
+public class RandomDataTest extends RetryTestCase {
+
+ public RandomDataTest(String name) {
+ super(name);
+ randomData = new RandomDataImpl();
+ }
+
+ protected final long smallSampleSize = 1000;
+ protected final double[] expected = { 250, 250, 250, 250 };
+ protected final int largeSampleSize = 10000;
+ private final String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "a", "b", "c", "d", "e", "f" };
+ protected RandomDataImpl randomData = null;
+ protected final ChiSquareTestImpl testStatistic = new ChiSquareTestImpl();
+
+ public void testNextIntExtremeValues() {
+ int x = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ int y = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ assertFalse(x == y);
+ }
+
+ public void testNextLongExtremeValues() {
+ long x = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
+ long y = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
+ assertFalse(x == y);
+ }
+
+ /** test dispersion and failure modes for nextInt() */
+ public void testNextInt() {
+ try {
+ randomData.nextInt(4, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency freq = new Frequency();
+ int value = 0;
+ for (int i = 0; i < smallSampleSize; i++) {
+ value = randomData.nextInt(0, 3);
+ assertTrue("nextInt range", (value >= 0) && (value <= 3));
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i = 0; i < 4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /*
+ * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 16.27);
+ }
+
+ /** test dispersion and failure modes for nextLong() */
+ public void testNextLong() {
+ try {
+ randomData.nextLong(4, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency freq = new Frequency();
+ long value = 0;
+ for (int i = 0; i < smallSampleSize; i++) {
+ value = randomData.nextLong(0, 3);
+ assertTrue("nextInt range", (value >= 0) && (value <= 3));
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i = 0; i < 4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /*
+ * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 16.27);
+ }
+
+ /** test dispersion and failure modes for nextSecureLong() */
+ public void testNextSecureLong() {
+ try {
+ randomData.nextSecureLong(4, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency freq = new Frequency();
+ long value = 0;
+ for (int i = 0; i < smallSampleSize; i++) {
+ value = randomData.nextSecureLong(0, 3);
+ assertTrue("nextInt range", (value >= 0) && (value <= 3));
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i = 0; i < 4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /*
+ * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 16.27);
+ }
+
+ /** test dispersion and failure modes for nextSecureInt() */
+ public void testNextSecureInt() {
+ try {
+ randomData.nextSecureInt(4, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency freq = new Frequency();
+ int value = 0;
+ for (int i = 0; i < smallSampleSize; i++) {
+ value = randomData.nextSecureInt(0, 3);
+ assertTrue("nextInt range", (value >= 0) && (value <= 3));
+ freq.addValue(value);
+ }
+ long[] observed = new long[4];
+ for (int i = 0; i < 4; i++) {
+ observed[i] = freq.getCount(i);
+ }
+
+ /*
+ * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 16.27);
+ }
+
+ /**
+ * Make sure that empirical distribution of random Poisson(4)'s has P(X <=
+ * 5) close to actual cumulative Poisson probability and that nextPoisson
+ * fails when mean is non-positive TODO: replace with statistical test,
+ * adding test stat to TestStatistic
+ */
+ public void testNextPoisson() {
+ try {
+ randomData.nextPoisson(0);
+ fail("zero mean -- expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ Frequency f = new Frequency();
+ for (int i = 0; i < largeSampleSize; i++) {
+ f.addValue(randomData.nextPoisson(4.0d));
+ }
+ long cumFreq = f.getCount(0) + f.getCount(1) + f.getCount(2)
+ + f.getCount(3) + f.getCount(4) + f.getCount(5);
+ long sumFreq = f.getSumFreq();
+ double cumPct = Double.valueOf(cumFreq).doubleValue()
+ / Double.valueOf(sumFreq).doubleValue();
+ assertEquals("cum Poisson(4)", cumPct, 0.7851, 0.2);
+ try {
+ randomData.nextPoisson(-1);
+ fail("negative mean supplied -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ randomData.nextPoisson(0);
+ fail("0 mean supplied -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ }
+
+ public void testNextPoissonConsistency() throws Exception {
+
+ // Reseed randomGenerator to get fixed sequence
+ randomData.reSeed(1000);
+
+ // Small integral means
+ for (int i = 1; i < 100; i++) {
+ checkNextPoissonConsistency(i);
+ }
+ // non-integer means
+ for (int i = 1; i < 10; i++) {
+ checkNextPoissonConsistency(randomData.nextUniform(1, 1000));
+ }
+ // large means
+ // TODO: When MATH-282 is resolved, s/3000/10000 below
+ for (int i = 1; i < 10; i++) {
+ checkNextPoissonConsistency(randomData.nextUniform(1000, 3000));
+ }
+ }
+
+ /**
+ * Verifies that nextPoisson(mean) generates an empirical distribution of values
+ * consistent with PoissonDistributionImpl by generating 1000 values, computing a
+ * grouped frequency distribution of the observed values and comparing this distribution
+ * to the corresponding expected distribution computed using PoissonDistributionImpl.
+ * Uses ChiSquare test of goodness of fit to evaluate the null hypothesis that the
+ * distributions are the same. If the null hypothesis can be rejected with confidence
+ * 1 - alpha, the check fails.
+ */
+ public void checkNextPoissonConsistency(double mean) throws Exception {
+ // Generate sample values
+ final int sampleSize = 1000; // Number of deviates to generate
+ final int minExpectedCount = 7; // Minimum size of expected bin count
+ long maxObservedValue = 0;
+ final double alpha = 0.001; // Probability of false failure
+ Frequency frequency = new Frequency();
+ for (int i = 0; i < sampleSize; i++) {
+ long value = randomData.nextPoisson(mean);
+ if (value > maxObservedValue) {
+ maxObservedValue = value;
+ }
+ frequency.addValue(value);
+ }
+
+ /*
+ * Set up bins for chi-square test.
+ * Ensure expected counts are all at least minExpectedCount.
+ * Start with upper and lower tail bins.
+ * Lower bin = [0, lower); Upper bin = [upper, +inf).
+ */
+ PoissonDistribution poissonDistribution = new PoissonDistributionImpl(mean);
+ int lower = 1;
+ while (poissonDistribution.cumulativeProbability(lower - 1) * sampleSize < minExpectedCount) {
+ lower++;
+ }
+ int upper = (int) (5 * mean); // Even for mean = 1, not much mass beyond 5
+ while ((1 - poissonDistribution.cumulativeProbability(upper - 1)) * sampleSize < minExpectedCount) {
+ upper--;
+ }
+
+ // Set bin width for interior bins. For poisson, only need to look at end bins.
+ int binWidth = 1;
+ boolean widthSufficient = false;
+ double lowerBinMass = 0;
+ double upperBinMass = 0;
+ while (!widthSufficient) {
+ lowerBinMass = poissonDistribution.cumulativeProbability(lower, lower + binWidth - 1);
+ upperBinMass = poissonDistribution.cumulativeProbability(upper - binWidth + 1, upper);
+ widthSufficient = FastMath.min(lowerBinMass, upperBinMass) * sampleSize >= minExpectedCount;
+ binWidth++;
+ }
+
+ /*
+ * Determine interior bin bounds. Bins are
+ * [1, lower = binBounds[0]), [lower, binBounds[1]), [binBounds[1], binBounds[2]), ... ,
+ * [binBounds[binCount - 2], upper = binBounds[binCount - 1]), [upper, +inf)
+ *
+ */
+ List<Integer> binBounds = new ArrayList<Integer>();
+ binBounds.add(lower);
+ int bound = lower + binWidth;
+ while (bound < upper - binWidth) {
+ binBounds.add(bound);
+ bound += binWidth;
+ }
+ binBounds.add(bound);
+ binBounds.add(upper);
+
+ // Compute observed and expected bin counts
+ final int binCount = binBounds.size() + 1;
+ long[] observed = new long[binCount];
+ double[] expected = new double[binCount];
+
+ // Bottom bin
+ observed[0] = 0;
+ for (int i = 0; i < lower; i++) {
+ observed[0] += frequency.getCount(i);
+ }
+ expected[0] = poissonDistribution.cumulativeProbability(lower - 1) * sampleSize;
+
+ // Top bin
+ observed[binCount - 1] = 0;
+ for (int i = upper; i <= maxObservedValue; i++) {
+ observed[binCount - 1] += frequency.getCount(i);
+ }
+ expected[binCount - 1] = (1 - poissonDistribution.cumulativeProbability(upper - 1)) * sampleSize;
+
+ // Interior bins
+ for (int i = 1; i < binCount - 1; i++) {
+ observed[i] = 0;
+ for (int j = binBounds.get(i - 1); j < binBounds.get(i); j++) {
+ observed[i] += frequency.getCount(j);
+ } // Expected count is (mass in [binBounds[i], binBounds[i+1])) * sampleSize
+ expected[i] = (poissonDistribution.cumulativeProbability(binBounds.get(i) - 1) -
+ poissonDistribution.cumulativeProbability(binBounds.get(i - 1) -1)) * sampleSize;
+ }
+
+ // Use chisquare test to verify that generated values are poisson(mean)-distributed
+ ChiSquareTest chiSquareTest = new ChiSquareTestImpl();
+ try {
+ // Fail if we can reject null hypothesis that distributions are the same
+ assertFalse(chiSquareTest.chiSquareTest(expected, observed, alpha));
+ } catch (AssertionFailedError ex) {
+ StringBuilder msgBuffer = new StringBuilder();
+ DecimalFormat df = new DecimalFormat("#.##");
+ msgBuffer.append("Chisquare test failed for mean = ");
+ msgBuffer.append(mean);
+ msgBuffer.append(" p-value = ");
+ msgBuffer.append(chiSquareTest.chiSquareTest(expected, observed));
+ msgBuffer.append(" chisquare statistic = ");
+ msgBuffer.append(chiSquareTest.chiSquare(expected, observed));
+ msgBuffer.append(". \n");
+ msgBuffer.append("bin\t\texpected\tobserved\n");
+ for (int i = 0; i < expected.length; i++) {
+ msgBuffer.append("[");
+ msgBuffer.append(i == 0 ? 1: binBounds.get(i - 1));
+ msgBuffer.append(",");
+ msgBuffer.append(i == binBounds.size() ? "inf": binBounds.get(i));
+ msgBuffer.append(")");
+ msgBuffer.append("\t\t");
+ msgBuffer.append(df.format(expected[i]));
+ msgBuffer.append("\t\t");
+ msgBuffer.append(observed[i]);
+ msgBuffer.append("\n");
+ }
+ msgBuffer.append("This test can fail randomly due to sampling error with probability ");
+ msgBuffer.append(alpha);
+ msgBuffer.append(".");
+ fail(msgBuffer.toString());
+ }
+ }
+
+ /** test dispersion and failure modes for nextHex() */
+ public void testNextHex() {
+ try {
+ randomData.nextHexString(-1);
+ fail("negative length supplied -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ randomData.nextHexString(0);
+ fail("zero length supplied -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ String hexString = randomData.nextHexString(3);
+ if (hexString.length() != 3) {
+ fail("incorrect length for generated string");
+ }
+ hexString = randomData.nextHexString(1);
+ if (hexString.length() != 1) {
+ fail("incorrect length for generated string");
+ }
+ try {
+ hexString = randomData.nextHexString(0);
+ fail("zero length requested -- expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ if (hexString.length() != 1) {
+ fail("incorrect length for generated string");
+ }
+ Frequency f = new Frequency();
+ for (int i = 0; i < smallSampleSize; i++) {
+ hexString = randomData.nextHexString(100);
+ if (hexString.length() != 100) {
+ fail("incorrect length for generated string");
+ }
+ for (int j = 0; j < hexString.length(); j++) {
+ f.addValue(hexString.substring(j, j + 1));
+ }
+ }
+ double[] expected = new double[16];
+ long[] observed = new long[16];
+ for (int i = 0; i < 16; i++) {
+ expected[i] = (double) smallSampleSize * 100 / 16;
+ observed[i] = f.getCount(hex[i]);
+ }
+ /*
+ * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 37.70);
+ }
+
+ /** test dispersion and failure modes for nextHex() */
+ public void testNextSecureHex() {
+ try {
+ randomData.nextSecureHexString(-1);
+ fail("negative length -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ randomData.nextSecureHexString(0);
+ fail("zero length -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ String hexString = randomData.nextSecureHexString(3);
+ if (hexString.length() != 3) {
+ fail("incorrect length for generated string");
+ }
+ hexString = randomData.nextSecureHexString(1);
+ if (hexString.length() != 1) {
+ fail("incorrect length for generated string");
+ }
+ try {
+ hexString = randomData.nextSecureHexString(0);
+ fail("zero length requested -- expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ if (hexString.length() != 1) {
+ fail("incorrect length for generated string");
+ }
+ Frequency f = new Frequency();
+ for (int i = 0; i < smallSampleSize; i++) {
+ hexString = randomData.nextSecureHexString(100);
+ if (hexString.length() != 100) {
+ fail("incorrect length for generated string");
+ }
+ for (int j = 0; j < hexString.length(); j++) {
+ f.addValue(hexString.substring(j, j + 1));
+ }
+ }
+ double[] expected = new double[16];
+ long[] observed = new long[16];
+ for (int i = 0; i < 16; i++) {
+ expected[i] = (double) smallSampleSize * 100 / 16;
+ observed[i] = f.getCount(hex[i]);
+ }
+ /*
+ * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 37.70);
+ }
+
+ /** test failure modes and dispersion of nextUniform() */
+ public void testNextUniform() {
+ try {
+ randomData.nextUniform(4, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ randomData.nextUniform(3, 3);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ double[] expected = { 500, 500 };
+ long[] observed = { 0, 0 };
+ double lower = -1d;
+ double upper = 20d;
+ double midpoint = (lower + upper) / 2d;
+ double result = 0;
+ for (int i = 0; i < 1000; i++) {
+ result = randomData.nextUniform(lower, upper);
+ if ((result == lower) || (result == upper)) {
+ fail("generated value equal to an endpoint: " + result);
+ }
+ if (result < midpoint) {
+ observed[0]++;
+ } else {
+ observed[1]++;
+ }
+ }
+ /*
+ * Use ChiSquare dist with df = 2-1 = 1, alpha = .001 Change to 6.64 for
+ * alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 10.83);
+ }
+
+ /** test exclusive endpoints of nextUniform **/
+ public void testNextUniformExclusiveEndpoints() {
+ for (int i = 0; i < 1000; i++) {
+ double u = randomData.nextUniform(0.99, 1);
+ assertTrue(u > 0.99 && u < 1);
+ }
+ }
+
+ /** test failure modes and distribution of nextGaussian() */
+ public void testNextGaussian() {
+ try {
+ randomData.nextGaussian(0, 0);
+ fail("zero sigma -- IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ SummaryStatistics u = new SummaryStatistics();
+ for (int i = 0; i < largeSampleSize; i++) {
+ u.addValue(randomData.nextGaussian(0, 1));
+ }
+ double xbar = u.getMean();
+ double s = u.getStandardDeviation();
+ double n = u.getN();
+ /*
+ * t-test at .001-level TODO: replace with externalized t-test, with
+ * test statistic defined in TestStatistic
+ */
+ assertTrue(FastMath.abs(xbar) / (s / FastMath.sqrt(n)) < 3.29);
+ }
+
+ /** test failure modes and distribution of nextExponential() */
+ public void testNextExponential() {
+ try {
+ randomData.nextExponential(-1);
+ fail("negative mean -- expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ randomData.nextExponential(0);
+ fail("zero mean -- expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ long cumFreq = 0;
+ double v = 0;
+ for (int i = 0; i < largeSampleSize; i++) {
+ v = randomData.nextExponential(1);
+ assertTrue("exponential deviate postive", v > 0);
+ if (v < 2)
+ cumFreq++;
+ }
+ /*
+ * TODO: Replace with a statistical test, with statistic added to
+ * TestStatistic. Check below compares observed cumulative distribution
+ * evaluated at 2 with exponential CDF
+ */
+ assertEquals("exponential cumulative distribution", (double) cumFreq
+ / (double) largeSampleSize, 0.8646647167633873, .2);
+ }
+
+ /** test reseeding, algorithm/provider games */
+ public void testConfig() {
+ randomData.reSeed(1000);
+ double v = randomData.nextUniform(0, 1);
+ randomData.reSeed();
+ assertTrue("different seeds", Math
+ .abs(v - randomData.nextUniform(0, 1)) > 10E-12);
+ randomData.reSeed(1000);
+ assertEquals("same seeds", v, randomData.nextUniform(0, 1), 10E-12);
+ randomData.reSeedSecure(1000);
+ String hex = randomData.nextSecureHexString(40);
+ randomData.reSeedSecure();
+ assertTrue("different seeds", !hex.equals(randomData
+ .nextSecureHexString(40)));
+ randomData.reSeedSecure(1000);
+ assertTrue("same seeds", !hex
+ .equals(randomData.nextSecureHexString(40)));
+
+ /*
+ * remove this test back soon, since it takes about 4 seconds
+ *
+ * try { randomData.setSecureAlgorithm("SHA1PRNG","SUN"); } catch
+ * (NoSuchProviderException ex) { ; } assertTrue("different seeds",
+ * !hex.equals(randomData.nextSecureHexString(40))); try {
+ * randomData.setSecureAlgorithm("NOSUCHTHING","SUN");
+ * fail("expecting NoSuchAlgorithmException"); } catch
+ * (NoSuchProviderException ex) { ; } catch (NoSuchAlgorithmException
+ * ex) { ; }
+ *
+ * try { randomData.setSecureAlgorithm("SHA1PRNG","NOSUCHPROVIDER");
+ * fail("expecting NoSuchProviderException"); } catch
+ * (NoSuchProviderException ex) { ; }
+ */
+
+ // test reseeding without first using the generators
+ RandomDataImpl rd = new RandomDataImpl();
+ rd.reSeed(100);
+ rd.nextLong(1, 2);
+ RandomDataImpl rd2 = new RandomDataImpl();
+ rd2.reSeedSecure(2000);
+ rd2.nextSecureLong(1, 2);
+ rd = new RandomDataImpl();
+ rd.reSeed();
+ rd.nextLong(1, 2);
+ rd2 = new RandomDataImpl();
+ rd2.reSeedSecure();
+ rd2.nextSecureLong(1, 2);
+ }
+
+ /** tests for nextSample() sampling from Collection */
+ public void testNextSample() {
+ Object[][] c = { { "0", "1" }, { "0", "2" }, { "0", "3" },
+ { "0", "4" }, { "1", "2" }, { "1", "3" }, { "1", "4" },
+ { "2", "3" }, { "2", "4" }, { "3", "4" } };
+ long[] observed = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ double[] expected = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 };
+
+ HashSet<Object> cPop = new HashSet<Object>(); // {0,1,2,3,4}
+ for (int i = 0; i < 5; i++) {
+ cPop.add(Integer.toString(i));
+ }
+
+ Object[] sets = new Object[10]; // 2-sets from 5
+ for (int i = 0; i < 10; i++) {
+ HashSet<Object> hs = new HashSet<Object>();
+ hs.add(c[i][0]);
+ hs.add(c[i][1]);
+ sets[i] = hs;
+ }
+
+ for (int i = 0; i < 1000; i++) {
+ Object[] cSamp = randomData.nextSample(cPop, 2);
+ observed[findSample(sets, cSamp)]++;
+ }
+
+ /*
+ * Use ChiSquare dist with df = 10-1 = 9, alpha = .001 Change to 21.67
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 27.88);
+
+ // Make sure sample of size = size of collection returns same collection
+ HashSet<Object> hs = new HashSet<Object>();
+ hs.add("one");
+ Object[] one = randomData.nextSample(hs, 1);
+ String oneString = (String) one[0];
+ if ((one.length != 1) || !oneString.equals("one")) {
+ fail("bad sample for set size = 1, sample size = 1");
+ }
+
+ // Make sure we fail for sample size > collection size
+ try {
+ one = randomData.nextSample(hs, 2);
+ fail("sample size > set size, expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ // Make sure we fail for empty collection
+ try {
+ hs = new HashSet<Object>();
+ one = randomData.nextSample(hs, 0);
+ fail("n = k = 0, expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private int findSample(Object[] u, Object[] samp) {
+ for (int i = 0; i < u.length; i++) {
+ HashSet<Object> set = (HashSet<Object>) u[i];
+ HashSet<Object> sampSet = new HashSet<Object>();
+ for (int j = 0; j < samp.length; j++) {
+ sampSet.add(samp[j]);
+ }
+ if (set.equals(sampSet)) {
+ return i;
+ }
+ }
+ fail("sample not found:{" + samp[0] + "," + samp[1] + "}");
+ return -1;
+ }
+
+ /** tests for nextPermutation */
+ public void testNextPermutation() {
+ int[][] p = { { 0, 1, 2 }, { 0, 2, 1 }, { 1, 0, 2 }, { 1, 2, 0 },
+ { 2, 0, 1 }, { 2, 1, 0 } };
+ long[] observed = { 0, 0, 0, 0, 0, 0 };
+ double[] expected = { 100, 100, 100, 100, 100, 100 };
+
+ for (int i = 0; i < 600; i++) {
+ int[] perm = randomData.nextPermutation(3, 3);
+ observed[findPerm(p, perm)]++;
+ }
+
+ /*
+ * Use ChiSquare dist with df = 6-1 = 5, alpha = .001 Change to 15.09
+ * for alpha = .01
+ */
+ assertTrue("chi-square test -- will fail about 1 in 1000 times",
+ testStatistic.chiSquare(expected, observed) < 20.52);
+
+ // Check size = 1 boundary case
+ int[] perm = randomData.nextPermutation(1, 1);
+ if ((perm.length != 1) || (perm[0] != 0)) {
+ fail("bad permutation for n = 1, sample k = 1");
+
+ // Make sure we fail for k size > n
+ try {
+ perm = randomData.nextPermutation(2, 3);
+ fail("permutation k > n, expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ // Make sure we fail for n = 0
+ try {
+ perm = randomData.nextPermutation(0, 0);
+ fail("permutation k = n = 0, expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ // Make sure we fail for k < n < 0
+ try {
+ perm = randomData.nextPermutation(-1, -3);
+ fail("permutation k < n < 0, expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ }
+ }
+
+ // Disable until we have equals
+ //public void testSerial() {
+ // assertEquals(randomData, TestUtils.serializeAndRecover(randomData));
+ //}
+
+ private int findPerm(int[][] p, int[] samp) {
+ for (int i = 0; i < p.length; i++) {
+ boolean good = true;
+ for (int j = 0; j < samp.length; j++) {
+ if (samp[j] != p[i][j]) {
+ good = false;
+ }
+ }
+ if (good) {
+ return i;
+ }
+ }
+ fail("permutation not found");
+ return -1;
+ }
+
+ public void testNextInversionDeviate() throws Exception {
+ // Set the seed for the default random generator
+ randomData.reSeed(100);
+ double[] quantiles = new double[10];
+ for (int i = 0; i < 10; i++) {
+ quantiles[i] = randomData.nextUniform(0, 1);
+ }
+ // Reseed again so the inversion generator gets the same sequence
+ randomData.reSeed(100);
+ BetaDistributionImpl betaDistribution = new BetaDistributionImpl(2, 4);
+ /*
+ * Generate a sequence of deviates using inversion - the distribution function
+ * evaluated at the random value from the distribution should match the uniform
+ * random value used to generate it, which is stored in the quantiles[] array.
+ */
+ for (int i = 0; i < 10; i++) {
+ double value = randomData.nextInversionDeviate(betaDistribution);
+ assertEquals(betaDistribution.cumulativeProbability(value), quantiles[i], 10E-9);
+ }
+ }
+
+ public void testNextBeta() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new BetaDistributionImpl(2,5));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextBeta(2, 5);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextCauchy() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new CauchyDistributionImpl(1.2, 2.1));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextCauchy(1.2, 2.1);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextChiSquare() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new ChiSquaredDistributionImpl(12));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextChiSquare(12);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextF() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new FDistributionImpl(12, 5));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextF(12, 5);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextGamma() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new GammaDistributionImpl(4, 2));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextGamma(4, 2);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextT() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new TDistributionImpl(10));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextT(10);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextWeibull() throws Exception {
+ double[] quartiles = TestUtils.getDistributionQuartiles(new WeibullDistributionImpl(1.2, 2.1));
+ long[] counts = new long[4];
+ randomData.reSeed(1000);
+ for (int i = 0; i < 1000; i++) {
+ double value = randomData.nextWeibull(1.2, 2.1);
+ TestUtils.updateCounts(value, counts, quartiles);
+ }
+ TestUtils.assertChiSquareAccept(expected, counts, 0.001);
+ }
+
+ public void testNextBinomial() throws Exception {
+ BinomialDistributionTest testInstance = new BinomialDistributionTest("");
+ int[] densityPoints = testInstance.makeDensityTestPoints();
+ double[] densityValues = testInstance.makeDensityTestValues();
+ int sampleSize = 1000;
+ int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
+ BinomialDistributionImpl distribution = (BinomialDistributionImpl) testInstance.makeDistribution();
+ double[] expectedCounts = new double[length];
+ long[] observedCounts = new long[length];
+ for (int i = 0; i < length; i++) {
+ expectedCounts[i] = sampleSize * densityValues[i];
+ }
+ randomData.reSeed(1000);
+ for (int i = 0; i < sampleSize; i++) {
+ int value = randomData.nextBinomial(distribution.getNumberOfTrials(),
+ distribution.getProbabilityOfSuccess());
+ for (int j = 0; j < length; j++) {
+ if (value == densityPoints[j]) {
+ observedCounts[j]++;
+ }
+ }
+ }
+ TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
+ }
+
+ public void testNextHypergeometric() throws Exception {
+ HypergeometricDistributionTest testInstance = new HypergeometricDistributionTest("");
+ int[] densityPoints = testInstance.makeDensityTestPoints();
+ double[] densityValues = testInstance.makeDensityTestValues();
+ int sampleSize = 1000;
+ int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
+ HypergeometricDistributionImpl distribution = (HypergeometricDistributionImpl) testInstance.makeDistribution();
+ double[] expectedCounts = new double[length];
+ long[] observedCounts = new long[length];
+ for (int i = 0; i < length; i++) {
+ expectedCounts[i] = sampleSize * densityValues[i];
+ }
+ randomData.reSeed(1000);
+ for (int i = 0; i < sampleSize; i++) {
+ int value = randomData.nextHypergeometric(distribution.getPopulationSize(),
+ distribution.getNumberOfSuccesses(), distribution.getSampleSize());
+ for (int j = 0; j < length; j++) {
+ if (value == densityPoints[j]) {
+ observedCounts[j]++;
+ }
+ }
+ }
+ TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
+ }
+
+ public void testNextPascal() throws Exception {
+ PascalDistributionTest testInstance = new PascalDistributionTest("");
+ int[] densityPoints = testInstance.makeDensityTestPoints();
+ double[] densityValues = testInstance.makeDensityTestValues();
+ int sampleSize = 1000;
+ int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
+ PascalDistributionImpl distribution = (PascalDistributionImpl) testInstance.makeDistribution();
+ double[] expectedCounts = new double[length];
+ long[] observedCounts = new long[length];
+ for (int i = 0; i < length; i++) {
+ expectedCounts[i] = sampleSize * densityValues[i];
+ }
+ randomData.reSeed(1000);
+ for (int i = 0; i < sampleSize; i++) {
+ int value = randomData.nextPascal(distribution.getNumberOfSuccesses(), distribution.getProbabilityOfSuccess());
+ for (int j = 0; j < length; j++) {
+ if (value == densityPoints[j]) {
+ observedCounts[j]++;
+ }
+ }
+ }
+ TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
+ }
+
+ public void testNextZipf() throws Exception {
+ ZipfDistributionTest testInstance = new ZipfDistributionTest("");
+ int[] densityPoints = testInstance.makeDensityTestPoints();
+ double[] densityValues = testInstance.makeDensityTestValues();
+ int sampleSize = 1000;
+ int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues);
+ ZipfDistributionImpl distribution = (ZipfDistributionImpl) testInstance.makeDistribution();
+ double[] expectedCounts = new double[length];
+ long[] observedCounts = new long[length];
+ for (int i = 0; i < length; i++) {
+ expectedCounts[i] = sampleSize * densityValues[i];
+ }
+ randomData.reSeed(1000);
+ for (int i = 0; i < sampleSize; i++) {
+ int value = randomData.nextZipf(distribution.getNumberOfElements(), distribution.getExponent());
+ for (int j = 0; j < length; j++) {
+ if (value == densityPoints[j]) {
+ observedCounts[j]++;
+ }
+ }
+ }
+ TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/TestRandomGenerator.java b/src/test/java/org/apache/commons/math/random/TestRandomGenerator.java
new file mode 100644
index 0000000..46ed416
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/TestRandomGenerator.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.random;
+import java.util.Random;
+
+/**
+ * Dummy AbstractRandomGenerator concrete subclass that just wraps a
+ * java.util.Random instance. Used by AbstractRandomGeneratorTest to test
+ * default implementations in AbstractRandomGenerator.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class TestRandomGenerator extends AbstractRandomGenerator {
+
+ private Random random = new Random();
+
+ @Override
+ public void setSeed(long seed) {
+ clear();
+ random.setSeed(seed);
+ }
+
+ @Override
+ public double nextDouble() {
+ return random.nextDouble();
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/UncorrelatedRandomVectorGeneratorTest.java b/src/test/java/org/apache/commons/math/random/UncorrelatedRandomVectorGeneratorTest.java
new file mode 100644
index 0000000..60f3b1f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/UncorrelatedRandomVectorGeneratorTest.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.DimensionMismatchException;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.stat.descriptive.moment.VectorialCovariance;
+import org.apache.commons.math.stat.descriptive.moment.VectorialMean;
+
+import junit.framework.*;
+
+public class UncorrelatedRandomVectorGeneratorTest
+extends TestCase {
+
+ public UncorrelatedRandomVectorGeneratorTest(String name) {
+ super(name);
+ mean = null;
+ standardDeviation = null;
+ generator = null;
+ }
+
+ public void testMeanAndCorrelation() throws DimensionMismatchException {
+
+ VectorialMean meanStat = new VectorialMean(mean.length);
+ VectorialCovariance covStat = new VectorialCovariance(mean.length, true);
+ for (int i = 0; i < 10000; ++i) {
+ double[] v = generator.nextVector();
+ meanStat.increment(v);
+ covStat.increment(v);
+ }
+
+ double[] estimatedMean = meanStat.getResult();
+ double scale;
+ RealMatrix estimatedCorrelation = covStat.getResult();
+ for (int i = 0; i < estimatedMean.length; ++i) {
+ assertEquals(mean[i], estimatedMean[i], 0.07);
+ for (int j = 0; j < i; ++j) {
+ scale = standardDeviation[i] * standardDeviation[j];
+ assertEquals(0, estimatedCorrelation.getEntry(i, j) / scale, 0.03);
+ }
+ scale = standardDeviation[i] * standardDeviation[i];
+ assertEquals(1, estimatedCorrelation.getEntry(i, i) / scale, 0.025);
+ }
+
+ }
+
+ @Override
+ public void setUp() {
+ mean = new double[] {0.0, 1.0, -3.0, 2.3};
+ standardDeviation = new double[] {1.0, 2.0, 10.0, 0.1};
+ RandomGenerator rg = new JDKRandomGenerator();
+ rg.setSeed(17399225432l);
+ generator =
+ new UncorrelatedRandomVectorGenerator(mean, standardDeviation,
+ new GaussianRandomGenerator(rg));
+ }
+
+ @Override
+ public void tearDown() {
+ mean = null;
+ standardDeviation = null;
+ generator = null;
+ }
+
+ private double[] mean;
+ private double[] standardDeviation;
+ private UncorrelatedRandomVectorGenerator generator;
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/UniformRandomGeneratorTest.java b/src/test/java/org/apache/commons/math/random/UniformRandomGeneratorTest.java
new file mode 100644
index 0000000..893bdbf
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/UniformRandomGeneratorTest.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.random;
+
+import org.apache.commons.math.stat.StatUtils;
+
+import junit.framework.*;
+
+public class UniformRandomGeneratorTest
+extends TestCase {
+
+ public UniformRandomGeneratorTest(String name) {
+ super(name);
+ }
+
+ public void testMeanAndStandardDeviation() {
+ RandomGenerator rg = new JDKRandomGenerator();
+ rg.setSeed(17399225432l);
+ UniformRandomGenerator generator = new UniformRandomGenerator(rg);
+ double[] sample = new double[10000];
+ for (int i = 0; i < sample.length; ++i) {
+ sample[i] = generator.nextNormalizedDouble();
+ }
+ assertEquals(0.0, StatUtils.mean(sample), 0.07);
+ assertEquals(1.0, StatUtils.variance(sample), 0.02);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/ValueServerTest.java b/src/test/java/org/apache/commons/math/random/ValueServerTest.java
new file mode 100644
index 0000000..f0f33a9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/ValueServerTest.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.random;
+
+import java.io.EOFException;
+import java.net.URL;
+
+import org.apache.commons.math.RetryTestCase;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+
+/**
+ * Test cases for the ValueServer class.
+ *
+ * @version $Revision: 1003907 $ $Date: 2010-10-03 00:23:34 +0200 (dim. 03 oct. 2010) $
+ */
+
+public final class ValueServerTest extends RetryTestCase {
+
+ private ValueServer vs = new ValueServer();
+
+ public ValueServerTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ vs.setMode(ValueServer.DIGEST_MODE);
+ URL url = getClass().getResource("testData.txt");
+ vs.setValuesFileURL(url);
+ }
+
+ /**
+ * Generate 1000 random values and make sure they look OK.<br>
+ * Note that there is a non-zero (but very small) probability that
+ * these tests will fail even if the code is working as designed.
+ */
+ public void testNextDigest() throws Exception{
+ double next = 0.0;
+ double tolerance = 0.1;
+ vs.computeDistribution();
+ assertTrue("empirical distribution property",
+ vs.getEmpiricalDistribution() != null);
+ SummaryStatistics stats = new SummaryStatistics();
+ for (int i = 1; i < 1000; i++) {
+ next = vs.getNext();
+ stats.addValue(next);
+ }
+ assertEquals("mean", 5.069831575018909, stats.getMean(), tolerance);
+ assertEquals
+ ("std dev", 1.0173699343977738, stats.getStandardDeviation(),
+ tolerance);
+
+ vs.computeDistribution(500);
+ stats = new SummaryStatistics();
+ for (int i = 1; i < 1000; i++) {
+ next = vs.getNext();
+ stats.addValue(next);
+ }
+ assertEquals("mean", 5.069831575018909, stats.getMean(), tolerance);
+ assertEquals
+ ("std dev", 1.0173699343977738, stats.getStandardDeviation(),
+ tolerance);
+
+ }
+
+ /**
+ * Make sure exception thrown if digest getNext is attempted
+ * before loading empiricalDistribution.
+ */
+ public void testNextDigestFail() throws Exception {
+ try {
+ vs.getNext();
+ fail("Expecting IllegalStateException");
+ } catch (IllegalStateException ex) {}
+ }
+
+ public void testEmptyReplayFile() throws Exception {
+ try {
+ URL url = getClass().getResource("emptyFile.txt");
+ vs.setMode(ValueServer.REPLAY_MODE);
+ vs.setValuesFileURL(url);
+ vs.getNext();
+ fail("an exception should have been thrown");
+ } catch (EOFException eof) {
+ // expected behavior
+ }
+ }
+
+ public void testEmptyDigestFile() throws Exception {
+ try {
+ URL url = getClass().getResource("emptyFile.txt");
+ vs.setMode(ValueServer.DIGEST_MODE);
+ vs.setValuesFileURL(url);
+ vs.computeDistribution();
+ fail("an exception should have been thrown");
+ } catch (EOFException eof) {
+ // expected behavior
+ }
+ }
+
+ /**
+ * Test ValueServer REPLAY_MODE using values in testData file.<br>
+ * Check that the values 1,2,1001,1002 match data file values 1 and 2.
+ * the sample data file.
+ */
+ public void testReplay() throws Exception {
+ double firstDataValue = 4.038625496201205;
+ double secondDataValue = 3.6485326248346936;
+ double tolerance = 10E-15;
+ double compareValue = 0.0d;
+ vs.setMode(ValueServer.REPLAY_MODE);
+ vs.resetReplayFile();
+ compareValue = vs.getNext();
+ assertEquals(compareValue,firstDataValue,tolerance);
+ compareValue = vs.getNext();
+ assertEquals(compareValue,secondDataValue,tolerance);
+ for (int i = 3; i < 1001; i++) {
+ compareValue = vs.getNext();
+ }
+ compareValue = vs.getNext();
+ assertEquals(compareValue,firstDataValue,tolerance);
+ compareValue = vs.getNext();
+ assertEquals(compareValue,secondDataValue,tolerance);
+ vs.closeReplayFile();
+ // make sure no NPE
+ vs.closeReplayFile();
+ }
+
+ /**
+ * Test other ValueServer modes
+ */
+ public void testModes() throws Exception {
+ vs.setMode(ValueServer.CONSTANT_MODE);
+ vs.setMu(0);
+ assertEquals("constant mode test",vs.getMu(),vs.getNext(),Double.MIN_VALUE);
+ vs.setMode(ValueServer.UNIFORM_MODE);
+ vs.setMu(2);
+ double val = vs.getNext();
+ assertTrue(val > 0 && val < 4);
+ vs.setSigma(1);
+ vs.setMode(ValueServer.GAUSSIAN_MODE);
+ val = vs.getNext();
+ assertTrue("gaussian value close enough to mean",
+ val < vs.getMu() + 100*vs.getSigma());
+ vs.setMode(ValueServer.EXPONENTIAL_MODE);
+ val = vs.getNext();
+ assertTrue(val > 0);
+ try {
+ vs.setMode(1000);
+ vs.getNext();
+ fail("bad mode, expecting IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // ignored
+ }
+ }
+
+ /**
+ * Test fill
+ */
+ public void testFill() throws Exception {
+ vs.setMode(ValueServer.CONSTANT_MODE);
+ vs.setMu(2);
+ double[] val = new double[5];
+ vs.fill(val);
+ for (int i = 0; i < 5; i++) {
+ assertEquals("fill test in place",2,val[i],Double.MIN_VALUE);
+ }
+ double v2[] = vs.fill(3);
+ for (int i = 0; i < 3; i++) {
+ assertEquals("fill test in place",2,v2[i],Double.MIN_VALUE);
+ }
+ }
+
+ /**
+ * Test getters to make Clover happy
+ */
+ public void testProperties() throws Exception {
+ vs.setMode(ValueServer.CONSTANT_MODE);
+ assertEquals("mode test",ValueServer.CONSTANT_MODE,vs.getMode());
+ vs.setValuesFileURL("http://www.apache.org");
+ URL url = vs.getValuesFileURL();
+ assertEquals("valuesFileURL test","http://www.apache.org",url.toString());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well1024aTest.java b/src/test/java/org/apache/commons/math/random/Well1024aTest.java
new file mode 100644
index 0000000..2130657
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well1024aTest.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.random;
+
+import org.junit.Assert;
+
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class Well1024aTest {
+
+ @Test
+ public void testGaussian() {
+ Well1024a mt = new Well1024a(42853252100l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 10000; ++i) {
+ sample.addValue(mt.nextGaussian());
+ }
+ Assert.assertEquals(0.0, sample.getMean(), 0.004);
+ Assert.assertEquals(1.0, sample.getStandardDeviation(), 0.003);
+ }
+
+ @Test
+ public void testDouble() {
+ Well1024a mt = new Well1024a(195357343514l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 10000; ++i) {
+ sample.addValue(mt.nextDouble());
+ }
+ Assert.assertEquals(0.5, sample.getMean(), 0.0006);
+ Assert.assertEquals(1.0 / (2.0 * FastMath.sqrt(3.0)),
+ sample.getStandardDeviation(),
+ 0.002);
+ }
+
+ @Test
+ public void testFloat() {
+ Well1024a mt = new Well1024a(4442733263l);
+ SummaryStatistics sample = new SummaryStatistics();
+ for (int i = 0; i < 10000; ++i) {
+ sample.addValue(mt.nextFloat());
+ }
+ Assert.assertEquals(0.5, sample.getMean(), 0.0001);
+ Assert.assertEquals(1.0 / (2.0 * FastMath.sqrt(3.0)),
+ sample.getStandardDeviation(),
+ 0.003);
+ }
+
+ @Test(expected=java.lang.IllegalArgumentException.class)
+ public void testNextIntNeg() {
+ new Well1024a(1).nextInt(-1);
+ }
+
+ @Test
+ public void testNextIntN() {
+ Well1024a mt = new Well1024a(0x12b8a7412bb25el);
+ for (int n = 1; n < 20; ++n) {
+ int[] count = new int[n];
+ for (int k = 0; k < 10000; ++k) {
+ int l = mt.nextInt(n);
+ ++count[l];
+ Assert.assertTrue(l >= 0);
+ Assert.assertTrue(l < n);
+ }
+ for (int i = 0; i < n; ++i) {
+ Assert.assertTrue(n * count[i] > 8600);
+ Assert.assertTrue(n * count[i] < 11200);
+ }
+ }
+ }
+
+ @Test
+ public void testNextInt() {
+ Well1024a mt = new Well1024a(new int[] { 1, 2, 3, 4, 5 });
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextInt() >= 0) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ Assert.assertTrue(FastMath.abs(walk) < 70);
+ }
+
+ @Test
+ public void testNextLong() {
+ Well1024a mt = new Well1024a(12345);
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextLong() >= 0) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ Assert.assertTrue(FastMath.abs(walk) < 70);
+ }
+
+ @Test
+ public void testNexBoolean() {
+ Well1024a mt = new Well1024a(76342);
+ int walk = 0;
+ for (int k = 0; k < 10000; ++k) {
+ if (mt.nextBoolean()) {
+ ++walk;
+ } else {
+ --walk;
+ }
+ }
+ Assert.assertTrue(FastMath.abs(walk) < 180);
+ }
+
+ @Test
+ public void testNexBytes() {
+ Well1024a mt = new Well1024a(0);
+ int[] count = new int[256];
+ byte[] bytes = new byte[10];
+ for (int k = 0; k < 1000000; ++k) {
+ mt.nextBytes(bytes);
+ for (byte b : bytes) {
+ ++count[b + 128];
+ }
+ }
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+ for (int c : count) {
+ min = FastMath.min(min, c);
+ max = FastMath.max(max, c);
+ }
+ int expected = (1000000 * bytes.length) / count.length;
+ Assert.assertTrue((expected - 600) < min);
+ Assert.assertTrue(max < (expected + 600));
+ }
+
+ @Test
+ public void testReferenceCode() {
+ Well1024a mt = new Well1024a(new int[] {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000,
+ -595495729, 1047766204, 1875773301, -1637793284, 1379017098, 262792705, 191880010, -251000180,
+ -1753047622, -972355720, 90626881, 1644693418, 1503365577, 439653419, 1806361562, 1268823869
+ });
+ int[] refInt = {
+ -1478749726, -1645579484, -2075363835, -2063444174, -1834148336, -1769045872, -40711346, 1717441026,
+ 2130656771, 783441285, 570433609, 1560023451, 653233971, 1368672434, -72036215, 1071111800,
+ 933776492, 26114960, 49888778, 1808107515, 1092989296, 754848337, 1336994364, -1987450448,
+ -691190146, -1803870839, 1110716866, 1173269113, -391000050, 2014216908, 180756301, -382891013,
+ -1908154585, 1580737629, 1080267957, -125532248, 2094530239, 2132964485, -438596348, -760299445,
+ 1058181869, 2050816800, -1534429037, -62552782, 824524142, -818590371, -1857695907, -684762866,
+ -156556543, -902759995, -880795194, -1387351132, -1263017515, 448006597, 201038266, 1929826313,
+ -455367306, 672963027, 2000073013, -1546842042, 446341090, 1001696686, -779919012, -347722602,
+ -1342821677, 1639571150, -835315755, 1505585376, 367004975, -2035864404, -1786623553, 1249724913,
+ 182435312, 1444514513, 1815333708, 1333772382, 299664001, -284691169, 2034403374, 1423310887,
+ -1319051884, 1557286441, -445198266, -251809030, 1602786123, 944036382, -1020529634, 258344235,
+ 685254367, 1838964943, -156674528, -979736602, -538312836, 234643178, 211152102, -635498640,
+ -1036733933, -1347589147, -565609042, -1358714165, 508618483, -786364693, 2071450261, 1206956772,
+ -678931458, 167690617, 144698821, 1719720781, 1575869280, -1343221123, -1766469944, 284991647,
+ -717305514, 892653651, -1368347075, -615701972, -730369849, 1360396003, -1869287623, 1778269052,
+ -586061545, -699517114, 61530249, -1860611767, -519660852, 1841085925, 1555610093, -399979337,
+ -790345742, 422355947, 2007965433, 2044952550, -1712164595, -102915702, -693865324, -1894042487,
+ -1285020072, -215883074, 95833252, 1625818040, -1055951680, 513067085, 1825246558, -553461652,
+ -1923361799, -1869480206, 567232636, -1751727150, -1832301399, -108136455, -1312244126, 14006795,
+ 850221366, -382389732, -1741556188, -1317464467, 1948314870, 753994471, 1028235947, 342494132,
+ -1862256693, 723808794, -234257642, 1609928369, -802733456, 1315831915, 1436072885, 1224767136,
+ 2144557791, -1839965886, 224821018, -1461697757, -1080386760, 1638573498, -1188173812, -325181523,
+ -1750676219, -1780415850, 698793362, -908352052, 299746482, -161660934, 1938166833, 800297005,
+ 56640033, -1214932666, -1248124842, 1822796868, 1777615881, -718517774, 1908159957, 1733053281,
+ 1851844331, 1283519375, -1771494956, 2060179999, 1666129209, 1919453531, -498145770, 697567008,
+ 1855487148, -1587163491, 565216434, -1477877933, -925662919, -806492585, -1201439047, -1424534232,
+ 1788616523, 69414717, 655893636, -1175978556, 24787512, -861550001, 439525754, -190433174,
+ -383811606, -508589783, 1441608687, 608181366, 1539467064, 925903122, 697209654, 1878283393,
+ -1967567432, -1659677763, -249658183, 847096354, 397741956, -125334541, -1286840731, 1016461908,
+ -997968592, 1795331475, 1856856501, -1716726445, -582181331, -887091847, 426964855, -609219941,
+ -1456232632, -483467616, 1069260754, 972242064, -1406786247, 1954194029, 52627891, 1212755081,
+ 2117436668, 281073392, 741537353, -483063506, 1850906286, -244876135, -270818140, 1817568823
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well19937aTest.java b/src/test/java/org/apache/commons/math/random/Well19937aTest.java
new file mode 100644
index 0000000..78e0bf5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well19937aTest.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.random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Well19937aTest {
+
+ @Test
+ public void testReferenceCode() {
+ int[] base = {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000,
+ -595495729, 1047766204, 1875773301, -1637793284, 1379017098, 262792705, 191880010, -251000180,
+ -1753047622, -972355720, 90626881, 1644693418, 1503365577, 439653419, 1806361562, 1268823869
+ };
+ int[] init = new int[624];
+ for (int i = 0; i < init.length; ++i) {
+ init[i] = base[i % base.length] + i;
+ }
+ Well19937a mt = new Well19937a(init);
+ int[] refInt = {
+ -612874471, -354976292, -1838197125, -1781560577, 278390997, 1214938280, -1752615390, -760835246, -1712883765, -241205782, -145390202, 495649160, -514388259, -1271015916, -1640000013, 849273623,
+ -549729394, -1206917255, -545909692, 811925434, -1665729633, -1525292882, 1416246482, -153220826, 1148868872, -326143196, 1724979062, 1790931148, -1648679618, -439051683, 112482777, -1484051520,
+ -1881272572, -1270447031, -1216102401, 1579107248, -1224395621, 2144411988, -416216641, -1529222361, 1628987080, 164445245, 1506928916, 928145916, 1436000427, 862025970, 560077705, -1887251027,
+ -514360858, 1735094506, 475624879, 1755802355, 295448361, -155399225, 3972415, 1368201076, -465126094, -1622687259, -246099304, 1798631152, -1937269102, -1700560396, -293352622, -896632303,
+ -2088933220, -194382452, -480218162, -1618517785, -1925031481, -150217434, 1678937261, 2130832364, -485546678, -1499224981, 1430390884, -1895417302, 210514746, 1781140999, -1940853105, -1238099647,
+ 485922557, -103223212, 633481679, -632946979, 695235541, -1661735272, 277603567, -958341538, 256982285, 1850270018, -327388076, -219053874, 1380560653, -1221689980, 1335863752, -545032383,
+ -575291735, -1295623907, -140058298, 1063302709, -1290702617, -790401546, -170630961, -1203114473, 1458063108, -1212753301, 1546428514, 2112636413, -1463028928, -1812598032, -883529486, 1131084094,
+ 62042165, 2135819802, -1192342739, 98361522, -1341042205, -475283063, -1632033747, 1745196892, 168608689, -914987039, 274428907, -881357258, 167940012, -1975737532, -903960486, -1370984244,
+ -589352935, 1783633514, -570111010, 71495377, 194463285, -1243905021, -1398490898, 221691209, -55728834, -638916786, -770622372, -1911651810, -295233027, 301467998, 2058638784, 681490183,
+ -1547865078, -1668135684, 1299261826, 1649468635, 287995017, -2076844852, 1193468826, -853948258, 120082777, 1051829542, -1288514343, -159456430, 275748820, -480127107, -604943233, -2138088332,
+ 1202614819, 1427201263, -1906344469, -1230779533, 1690367192, 733159097, 794410312, -1114452505, -1601554413, 976747949, 1517787154, 2091780205, 1052078906, 1919282771, -191013374, 1805397142,
+ 736939268, -1056272823, -727464316, -659459005, 797803875, -1104633884, 1042342081, -24514837, 1919469940, 1903722546, -814157872, 1605407665, -262351256, -288949635, 729204844, -1132605534,
+ 745453338, 387915035, 1094173337, 2100279147, 156863702, -257377544, -719587984, -1496015613, 1908993744, 2016957554, 918749666, -135963651, -1356808639, -1711185741, 1472589240, -398100149,
+ 628791415, -1381837652, -1820702771, -593586943, -1456631279, -1837975351, -1394249972, -556916726, 833231177, 43449750, 1029237092, -2086437337, -459463076, -533031784, -1739648287, -1374722961,
+ 2024908394, 1389678488, 2018558, -1391707864, -795935743, 904816957, 836583280, 1766194531, -1374431014, -904437876, 2030248636, -265724199, 2056758426, -810499837, 887193593, -77811488,
+ 1496312336, -1874348275, -456193866, -2137130942, 868120387, 29025455, -1999867716, 2001322335, -579152815, -390892056, 1592011837, -306394879, 93636886, -190879994, 1923358153, 269052141,
+ -396050253, -987531729, 480350991, 1276744541, -1445571957, -957571005, -2046270221, -1715395752, 1113585628, -1782113514, -697560146, 835320000, 1014320959, -2119834109, 460056841, -1464772991,
+ -1282790418, -2120806165, 86176097, -731086307, 832497517, -1876684928, 541008240, 551124479, -450919132, 647860281, -2115397586, 979247589, 1095559204, 1927958688, 169497703, 1999579054,
+ 2019745038, 1656022059, -1109662138, 375237154, 1450814436, 919988416, 849761266, 1457057327, 1771166577, -1639880487, -852488298, 1767063646, 657295386, -585561879, 740792583, 1664558308,
+ -654749506, 1109275990, 182597559, 1106789745, -1806628480, 25948116, 1748374299, 196057325, -164213209, 1687024594, 782029276, 1879737947, -1528219611, 412585737, 1190325629, 1985821911,
+ -1272945202, -1238637137, 465818730, -1537670961, 1131953615, 905623579, 609183424, 1138422991, 1522974699, 589719061, -1310894604, 890952933, -885204790, -393535694, 1238408670, 1780660354,
+ 677803525, -1121509064, 1553148616, 1109165936, -1450120385, 1525252521, -1354897489, -595402189, -1274551767, -869281409, 1788815975, 2020262116, 1124100185, -400839020, 310574108, 1354413045,
+ -1310514485, 1895732085, 626639054, 1667355357, 2065637178, -1889009143, -440157749, 1762849463, -1693853642, -56602956, -930874188, -398470740, 778356402, -2113156881, 42854964, 1844399604,
+ -2098310302, -1812029757, 1441188713, 899579267, 1266994172, 1841370863, -660740252, -43254718, 1124500192, 1884907320, 879997211, 1775139845, -1360112721, 1630490057, 362567879, 1113475029,
+ 290319279, -1209506867, 398146039, -957742350, 1185761854, 1519676447, -912689915, -1117128973, -305563462, -1928033363, -1766324543, 1702753492, 1696951912, -1895072395, 932663591, -566548128,
+ 991675996, 56529814, 980735023, 718166662, -650028466, -886842051, 1857048587, -569023569, -1820572202, -851452711, -958700452, -621825633, -65649888, -510143183, 761267599, -1692108035,
+ 1729071710, 1623630864, -53498654, 267235687, 659201413, 1152882627, -824194574, 356636960, -502391121, -538453360, 66115376, -1633290370, -1522088932, 268949070, 684499443, -859474501,
+ 1586764345, -1515639709, 319695602, -307025150, 69076508, 1050726785, -1340930110, 552191600, -207852941, -273572993, -539580440, 710343120, 1957076127, -1107172811, -561518280, -1775022699,
+ 1978792904, 1935531451, -2084046304, -419742902, -737652926, 614022023, 1676952428, 769892939, -1092786807, -1117113223, -266029995, -350150999, 207738542, 1964896575, 48805284, 1736500159,
+ 551289617, -1847923501, 1856609505, 2007480480, -681860219, -1198106493, 1483591043, -523895316, -1814473078, -1521087404, -1348859926, 1298056897, 1813789478, 946683654, 79196400, 1766250931,
+ 472737685, 1764634332, -1844726079, -130619045, -508713868, -1762537125, 1010108863, 170107098, 1705386380, -1139681802, 183739097, 1662699401, 1842694501, 1714633805, 46208876, 616720693,
+ -252553427, 1986302230, -103130254, 1943225981, 110746655, 553260552, 1588938073, -1934623163, -2144781332, -2086217416, 1941265852, -781953226, 1216234254, 605543697, -710872598, 2048636577,
+ -1986927728, -1007017623, 1243051501, -614249563, -2128221291, 581579813, 1173464240, -1906830937, 261329601, -1805974103, 769823490, 1858731164, -561762071, 516417430, -1221329437, -825500715,
+ 1091364656, -993658663, -1475434188, -1070804384, -1876492082, 899494424, 683486936, 878807455, 56642807, -1268202879, 1379172046, -1386869373, -1158233876, 1759190552, 1597629789, 1411151497,
+ -1254268471, 1075936979, -918778269, -2132675184, 953140888, 1906982077, 1154200766, -365384600, -1142488826, 708535121, -2134869964, -1531201665, -2100526761, 1268256467, 2071480803, 193135243,
+ 1374158182, 989505347, -933612202, -2134839213, -1302795271, -2092029041, 1812014826, 2090855917, 2005348528, 606434393, -60141386, 11156360, 539516285, -122485034, -893237911, -978127424,
+ 1509901816, -451029719, 428544700, -1622965963, -1993611605, -1989324583, 1104111587, -795138585, -899552401, -2110167769, -234502445, 1586963605, -503778455, 529261062, 325327284, -106186403,
+ 65369563, -1475700698, -228624261, 715975009, 1099352363, -1796883396, 1376542700, -308942420, -344940451, -395389249, -1562737166, 1869802677, 1273494710, 2075587668, -789570273, 1563347596,
+ 1142901755, 1676422422, -1729157809, -1399423717, -1814262429, -1809707284, 1393992342, -570246212, 1065528749, -781643849, 1218667301, -1097949471, 1305629790, 901301039, -704762030, 360582612,
+ 1411910672, 1848068741, -614500891, -146889637, -913903597, 723527277, -147033328, -199273155, 734997691, -2072735286, 2129258691, -1385074104, 931616624, 1065477319, -1543474555, -531410292,
+ -2123119121, -1538464113, -1153585193, 1559931968, -654877011, 879865200, 1489681397, 1998864644, -1964160144, 163671782, -858364148, -323324233, 801208648, 705864113, 436184243, 643773864,
+ 2087594507, 134637265, -749956494, -1657343972, -1828172168, -27357303, -1145161336, -1192513644, 216148260, 611393153, -13752671, -358631090, -1211920749, 593572064, 657629904, -1445961088,
+ -250704995, 1797542707, -2122311891, -316774825, -296303057, -868002056, -86697533, 2020588145, 1203427903, -1371839056, 669531557, -2031033836, 1323994690, 13703036, 785437772, -1465821554,
+ -694756014, -2131068154, -1745448876, -1095891733, 936594025, -1119068454, 855423970, 1705079340, -905640608, 162297141, 1336619311, -344353769, -92608588, -1080573824, 2002293105, -2088030765,
+ -1684198727, -129054718, -949437132, -127983221, -216664110, 1700146143, -711174649, 1500113839, 1212236226, -2017364219, -1263597675, 511929344, -323998524, -2021313185, 1803000924, 927670608,
+ 336267187, 1244256964, -1665390108, 991395134, -251232188, 1267445783, 1547951569, 740269916, 1776431169, 1687220659, 228229817, 271386089, -682906779, -438090344, 1190436796, -744272540,
+ 1879221151, 1145200306, -1730983338, -1119209309, 90826726, 1567861540, 1638852830, -1645384932, 1566909531, 1088584561, 1030555565, -1872052014, 720320695, -885053674, -321216789, 739907579,
+ 368580703, -443635520, 1232705619, -1355949988, -1047211249, -1571429448, 599299852, 1036970439, 1513838571, -51797291, -26647565, -1262878942, -916262582, 1579082269, -292007383, 1289013866,
+ -1612184284, 1451738668, 448608569, 476432992, -1229609565, 786372409, 929928149, -150100614, 448155944, -1322320576, -856549627, 1057443268, -1536809554, 577508258, 584906122, 275295163,
+ -604262071, -236043234, -1866434954, -2072447013, 646132876, 847562546, -310005953, -1104162658, 393261203, -730102354, 440824482, 1654535035, -1296359745, 1487359328, -977776604, -775827779,
+ -1298695106, 519080622, 1697162240, 227873031, -371123123, 1273702312, -1710063656, -2138342344, 1139555478, 1531578907, -1498880699, 1183507362, 1875307493, -1649740413, 2135386504, -962458407,
+ 424161768, 504272962, 202204247, 1783466420, 2015579232, -676642965, 2067456450, 914480415, -620398841, 1880405399, 1406637142, 1951104977, 633496157, 224861869, -58659291, 994942775,
+ -479000645, 1421449115, 100168104, 249754169, -1219011494, 1736303638, 364013694, -1750035055, -479217141, 1652913106, -2109452331, 1633842910, -1547663337, 936627493, -1152799743, 896955899,
+ -1407742850, -523769014, 357161414, 872293304, 744895980, 720829676, -240843156, -111779524, 1292836315, -1792141538, 1946959925, 1181751089, -1120674052, 1185192575, -1387002557, 1973209255,
+ -120887476, -766577735, -443913073, 786620227, 428564781, -101232106, -425959852, 198082021, 1173272226, -1744840378, -1621135606, -1539498583, -1101274572, 43399711, -1256764602, 1201920787,
+ 2049426139, 846545551, -2121520873, -1202939675, -470425740, 321987390, 1862019060, -951540342, -894238318, -430407175, -1662746491, 656574776, 1580373777, -431290218, 1645824323, -1953526979,
+ -374682356, 474291752, 1071558425, 511038981, -760598678, -567797285, -1176476266, -268409005, -2130644484, -67970563, 1756046948, 1429860462, -1130984739, -124916495, -1544436836, -1863524031,
+ 1024916487, -1388636482, -1573205065, 892628956, 1831270021, 1176430590, 1158914682, -2006787098, -1228130033, 1516111488, -1499151347, 470546266, 1642603981, 1425140838, -1823071475, -1775267236,
+ -1009380612, 164746986, 1129677098, 1842642579, -482342932, -507480364, 1656012309, 1981601761, -881042120, -511987083, 342447017, 381192578, 983008095, 741012865, -1877136350, -199211983,
+ -452784912, 1929572576, -1678291139, -864375281, -1610561247, -1936356726, -749553767, -865893512, -567081879, -1303973729, -939636958, -622974563, 428284937, 1049237414, 852280765, 86648946,
+ -1353851401, -1045422335, 898035731, -1636093996, -1083174191, 245046915, -359768226, -1028491655, 1051575118, 1774289451, 1839389415, -1594053468, 736707953, 1873556950, 401186168, -583669552,
+ -88375334, 2002752071, 264506453, -1304812107, -759203942, -114958524, -1878903503, 841613720, 1910863820, -1738114003, 701455920, 1791058048, -1850960547, 1672292671, 1172188809, 604848896,
+ -1607489375, 305370478, -948153885, -1971080100, -1848966954, -584538365, 39416319, -1689119162, 944942598, 1777111075, 1534005553, 2022718432, -25820385, 3077695, -315950520, 1859184648,
+ -1397829266, -1666371809, 858913807, -610818620, 1554973298, 580023809, -1662988256, -408630026, 1316681876, 738204271, 942829881, -758486983, 780345857, 667165037, -2086803585, 789741324
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well19937cTest.java b/src/test/java/org/apache/commons/math/random/Well19937cTest.java
new file mode 100644
index 0000000..1f40fa4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well19937cTest.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.random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Well19937cTest {
+
+ @Test
+ public void testReferenceCode() {
+ int[] base = {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000,
+ -595495729, 1047766204, 1875773301, -1637793284, 1379017098, 262792705, 191880010, -251000180,
+ -1753047622, -972355720, 90626881, 1644693418, 1503365577, 439653419, 1806361562, 1268823869
+ };
+ int[] init = new int[624];
+ for (int i = 0; i < init.length; ++i) {
+ init[i] = base[i % base.length] + i;
+ }
+ Well19937c mt = new Well19937c(init);
+ int[] refInt = {
+ 2128528153, 327121884, 935445371, -83026433, -1041143083, 2084595880, -1073535198, -1678863790, -523636021, -1514837782, -736786810, 1527711112, -1051227939, 978703380, 410322163, 1727815703,
+ -648426354, 636056441, 1954420292, 17754810, -958628705, -1091307602, 1793078738, -1680336346, 1792171272, 941973796, -2066152330, -1248758068, -1061211586, 262020189, 1276960217, -233886784,
+ 1767509252, -1811939255, -406116097, -742435920, -1349799525, 240329556, -332161345, 1488943143, -332244280, 2093328957, 674753300, -1930135556, 257111467, 63793650, -1964335223, 1315849133,
+ -797349146, 1372022250, -1451892049, -1325138957, -870401239, -1294317369, 91490879, 386205044, -704074702, -1230679067, 1513674392, -262996240, 1196007314, 1398903796, 803719762, -1750926831,
+ -1268814180, 1233515404, 1498313934, -970591257, 611113671, -261632474, 1834097325, 1709440492, -150396854, 2120561003, -62645660, 479080234, 1535125050, 1823378695, -1129289329, -1095198399,
+ 2092564733, 78836308, -692015409, 1647147229, -1847922219, 1838279320, -848333841, -1375151778, 920238861, 1512628290, -749439404, 288851918, -427218675, 679640964, 425700808, -2077624511,
+ -1929434455, -647176419, 650437190, -1926749131, -1564744729, 734494454, 108193743, 246246679, 810042628, 1952337771, 1089253730, -1874275331, 1428419392, -492969232, 1945270770, -201265602,
+ -755490251, -624426214, -699605715, -113446478, 809091299, -1521531511, 1136505389, -523660964, 132928433, 1926559713, -1485314325, -508322506, 46307756, -1627479740, -589386406, -1855555892,
+ 584299545, 1272841066, -597242658, 925134545, 1102566453, -753335037, -9523218, -1778632375, 568963646, 764338254, 1259944540, -2000124642, 1307414525, -151384482, 807294400, 1993749511,
+ -15503094, -709471492, 2104830082, 1387684315, -1929056119, 224254668, -733550950, -889466978, -1987783335, -437144026, 995905753, -1021386158, -1096313388, -1014152835, -1303258241, 1201884788,
+ -1845042397, 1421462511, 980805867, 2143771251, 481226968, 1790544569, 328448328, 1995857639, -66668269, -1411421267, -222586606, 866950765, -308713926, -1048350893, 993222402, -1139265642,
+ -871837948, 1145571913, 381928580, 35386691, 1640961123, -1192981020, 775971009, 594246635, 1603197812, -575106766, 2023682000, -1636301903, -718093720, -1666421635, -2146115988, 320593570,
+ 287355418, 454400027, 1112753817, 1751196267, 782077910, -1478447368, -1007557264, -862315517, -2035355952, 2123515250, -557641502, -1789932035, 879640129, 44167603, 791148984, 1382939723,
+ -2135684233, 1825489580, 937345485, -1839983359, -1536880111, -1472578359, 1548052748, -1471535862, -14508727, 1509621398, -2134967452, -787485401, 815341660, -327905128, 1028096737, 866906991,
+ -1585990806, 859229080, 234806270, 998518056, -1897890815, -900923587, 1179856752, 1529572451, 620486106, 1119836556, 1661285564, 2097404633, -1437490790, 265306115, -984880135, 1326751968,
+ 1280043536, 680210701, 155786166, 1550973250, -325781949, -597789777, -1939780, 1345275487, 1930450001, 941449704, 669301309, 693651713, -990721514, 582968326, 976132553, -1892942099,
+ -1065070157, -711990993, -688974833, -1026091683, 1115346827, -1305730749, -1733626381, -364566696, -21761572, -37152746, -262011730, 1302722752, -1806313409, -767072509, 764112137, 1671157377,
+ 1837645038, -1021606421, -1781898911, -232127459, -310742675, -1818095744, -1128320656, -705565953, -354445532, -523172807, -433877202, 131904485, -64292316, 381829280, 229820263, 1797992622,
+ 1359665678, 978481451, -885267130, -1415988446, -356533788, -961419072, 1938703090, 708344111, 679299953, 744615129, 1328811158, 1257588574, 569216282, -753296151, -1519838713, 2016884452,
+ 1062684606, 1561736790, 2028643511, -1353001615, 886376832, 1466953172, 1664783899, 1290079981, -57483993, -1176112430, 1634916316, 1976304475, 1374136869, -648738039, 1058175869, -909000745,
+ -1526439218, 726626991, 2066596202, 64980943, -26166577, -885900005, -1821546816, -1103727665, 730606315, -1324948459, -696956940, -1300869403, 1171578314, 797249074, -1600611618, 1928247682,
+ 307164165, -1482476232, -1886179640, 1306433392, 1945271359, -1272113751, -1285984081, -2057145549, 795047465, 1262569087, -1239828121, 1426641636, -786371495, 2120199316, 1273690652, 74457589,
+ -1033394229, 338952565, 46122958, 1225741533, 2115308090, 678200841, -1618264885, -101162569, -1628976330, -1232839500, 468709044, 1876019116, 92723122, 233398255, -554960844, 38494196,
+ -406437278, 2083528643, -1106878615, -340722557, -2123964932, 223183343, 108918116, -1014629054, -901344544, -838896840, -1908460517, -1763508731, -926890833, 1703791049, -667755577, 1694418389,
+ 791641263, 1095689677, 1119202039, -1419111438, -2012259010, 188017439, -1775110395, -1971099661, -1688113734, 131472813, -776304959, 1511388884, 2080864872, -1733824651, 1992147495, 1119828320,
+ 1065336924, -1357606762, 462963503, 1180719494, -202678962, -892646595, 605869323, 1144255663, 878462678, -1051371303, 872374876, 631322271, -172600544, -1552071375, -1939570033, 151973117,
+ 1640861022, 310682640, 34192866, 2057773671, -2004476027, -1879238973, 582736114, 900581664, -427390545, -1232348528, -535115984, 1321853054, 69386780, -1729375922, 1418473715, 1022091451,
+ 496799289, -80757405, -1903543310, -1128846846, 1703964, 1984450945, 856753858, -812919184, 775486323, -1376056193, 638628840, 314243536, 1030626207, 644050997, 73923896, 362270613,
+ 236584904, 1463240891, -223614432, 435371594, -751940030, -124274553, -1991092884, 1579624267, 1249632649, 157589625, -345229739, -366245207, -1399995986, 1651729983, 1965074340, -1108970305,
+ 1163690769, 1732013523, -1461252895, 669755552, -476503675, -264578685, -32813949, 288222188, -25734262, 106040916, 1654395626, -365148479, 2014455846, -2040447994, 1351639280, -919975757,
+ -1970412139, -47306532, 222377665, -363434917, -1091717516, 2090685531, -1221091649, -1729649190, -1239406708, 1064945398, -105437479, -419675255, 74701669, -12862899, -498269844, 1566898997,
+ -1872838355, 1596887574, 485902962, 469225597, -881763553, 1307841032, -1642872487, 1388543045, 379792876, 1095683384, 840780732, 1934378038, 1851278350, -1359389423, 130868458, -313448799,
+ -663624816, 1031714153, -608443411, -205137499, -1849464427, 1973593637, 1068741808, -1420655961, 1188762305, 954044841, -995454462, -1818101092, -1937201943, -324541290, -1520603933, 572873173,
+ -554764496, 1051557081, -1245136076, -985349536, 329320398, 1787901464, -37803304, -1759310177, -1463492617, -1861729663, 1251768782, 256937091, -779036948, -2049893864, 1256022877, 1228075657,
+ -1550195255, -611319853, 1190797155, 2047604112, -576077160, -1532843331, -1324899394, -159729560, -622525946, -1080302767, -236033484, 1895243903, -410123689, -1944154157, -681781021, 1208453003,
+ 579595878, 1303914051, -145607082, -131567277, -1917288455, 894217359, -175688726, -1585480723, 663691440, -1140068263, -641711178, 1596080008, 629197693, 976422358, -1570451095, 525923776,
+ 895046136, -504151767, 1602553020, -1233054923, -1798474837, -1488857895, 1055782627, 261863143, 1879276655, 488240679, 1910982611, -1919441259, 370435945, 1265230086, -1293284428, -1503576227,
+ 2076963035, -1379628250, 1157098875, 1984461153, -1947837397, 1705880124, 1453607404, -1233649748, 1479943773, -863878721, -862415630, -736723275, 940306358, -1596000684, -1174889953, -615723892,
+ -885006597, -1796723178, 1844159055, -188942309, 2107251811, -1675486996, -1009475178, -859263556, -431866963, -9593673, -1878920923, -104853791, -1535224994, -69315537, 586690130, -1292234796,
+ 1378749456, -301873019, -319297563, 1677205851, 292450579, -1289441171, 1788113680, 1907606333, 1464711611, -1372023606, -1978832445, -1772259768, 1949124464, 1818322887, -1138036603, 1249727628,
+ -1474866449, -1868013169, -1384567593, 717007936, 954189997, -1900561040, 738470389, -158973180, 1732860784, 1936031206, -1133354740, -1173166665, 1432976712, 852636081, 1732064691, -1831788120,
+ 1273933579, 455403217, 1988395890, 106493468, 506092152, -610530423, 1698053512, 1311747476, 1969503012, -1887461759, 1613543073, 903200334, -737865837, 325656800, -1234001200, 1492148864,
+ 2009861533, -368262605, 1091338541, 2076108119, -961392337, 1835877112, 316250307, -853333391, -2125443777, 815363504, -798707803, -158146540, 690786114, -530775684, 1203556940, 1611485582,
+ -1661412270, -53184506, 2126287444, -232222229, 1559486057, 283532250, 1202760418, 932144172, 1082594656, -570104011, 413509167, -995027177, -996477516, -540544, -745537167, -712135469,
+ -996294983, -592787198, 1889840948, 1314628747, -394266926, -682316577, 456447239, 1728806063, -396279614, -43387643, 1915717013, -861574144, -1078710588, -561401249, 1111464540, 63643984,
+ -1693870413, -968369980, -1053148188, 708799038, 1883537988, 373371671, -156410415, -1596483236, -1846890431, 888692915, -1025632583, -1666477591, -343066267, -2059058792, 641501628, -1744347292,
+ 1648632991, 1743540146, 2020952406, 164014499, 990508262, 1706408228, -1236471842, -347116260, 1843634523, 827255665, 300519853, -1265974830, -547247177, -583064554, -1995437077, 689210107,
+ -93151393, 835365056, 1706367315, -1605902756, 200954895, 431093688, -277573364, -928486713, -552221973, 145432789, 1128919795, 1675095586, 1930359882, 1215849501, -1447770583, 657776490,
+ 1885869860, -1629237204, -868897479, -1258169760, 1828140195, -883850439, 463933909, -347361158, 1478116648, 801176896, -1501915899, 1017335748, -1654508882, 123994786, 1588785290, 791166651,
+ -1523108535, 340411166, -496474762, -1189711141, -7392628, 2045171250, -1245366209, 834787230, -1346883181, 2034209454, 737043362, 898803323, 1983089087, -1845404320, 9585188, -1180608323,
+ 1665100606, 1949474222, -211115008, 1151308295, -2132174259, 913126312, -2085061672, 1419864120, -1134542954, -53833957, -246913211, 468382370, -1759479323, 1136686211, 1307012488, -2036299559,
+ -1346099736, 314743106, -1683101865, -947151948, -234529696, -2103334293, -279256894, -1484257, -1053953017, 1801205399, 941594454, -874119215, -672865187, 762284205, -1494975451, 486607927,
+ -898264389, -1711861093, -212572760, 2106484281, -1610786470, 1352525590, -837779586, 1568282001, -593019125, -1146260782, -1595879979, -640781858, 1107692311, 1547132709, -1928385535, -2057772805,
+ 634887038, 329772618, 942136006, -864405576, 501883884, 1537141484, -1180626836, 1123055420, 1090885851, 421662750, 2033111605, 1710917425, -1118058244, 74321279, 257328195, -1199940697,
+ 208625996, -442341447, 808119183, 1166827075, 1177417517, -1856155370, -1464837036, -60624923, -1306220638, -91104698, -1434621430, 548899241, 37351476, 1478278431, -1255061434, 248470035,
+ -104642597, -1865169521, 1418373655, -1660810523, -2129015436, 154612798, 276575732, 1930338442, 179503250, -929294855, -39452027, -1377657544, 1442322193, 1137511318, -432158653, -984801987,
+ 743099148, -1118893528, -904123623, -1273146363, -1884800406, -803169061, 1254123158, -484252077, 317646844, 404246525, -1230293916, 1121445742, -19657507, 652967153, -1055406692, -468950719,
+ -1493532921, -1447624258, -1369679689, -1517000228, -145853307, 1518006526, 1591195514, -1475557146, -909722097, 2103182976, -406830579, -2124025254, -1804819507, -1357512858, 567321869, 409048156,
+ 567805180, 1749009386, 1762759722, -1770324077, 1271140844, 468219092, 955792405, 1911965665, 1876314424, -718200715, -1278883927, 1392281730, -120519585, 851473793, 245054754, -33369039,
+ -284877584, -479534880, -212346563, -122017521, -1461429983, 1331007370, 1788621721, 1739036536, 1446350953, -1985448033, 685528610, -1386434659, 1368233993, 2021786790, 1596478397, -1716635278,
+ -2011083017, 171876097, -311529197, 687812052, 377000657, -1055547517, -1499047842, -1818434951, -120863666, 33888043, -1387509273, -541540700, 1162597745, -1331415338, 1931708792, -850270000,
+ 663845594, 1536495943, -322924971, -1380272203, 261190298, -204874428, -2104974031, 883819928, 155808204, -1454446035, 1323388464, -1696505728, 1549800285, 1018150463, -1327715703, -1582480640,
+ 1013659809, -1820360082, 1666498787, 1406120540, -196541482, 1248470531, -1250433281, 836375878, 177646854, -1927020253, 2145878321, 689712096, -596605921, 348283199, 1916993096, 481356808,
+ -339687826, 1219340319, 718895887, -2007521340, -1859185806, 2042164737, -58146784, 742449142, 1930754708, 780832111, 715056441, -1393886151, -8150527, -599607443, -537300865, -1212516084
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well44497aTest.java b/src/test/java/org/apache/commons/math/random/Well44497aTest.java
new file mode 100644
index 0000000..6618ac1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well44497aTest.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.random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Well44497aTest {
+
+ @Test
+ public void testReferenceCode() {
+ int[] base = {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000,
+ -595495729, 1047766204, 1875773301, -1637793284, 1379017098, 262792705, 191880010, -251000180,
+ -1753047622, -972355720, 90626881, 1644693418, 1503365577, 439653419, 1806361562, 1268823869
+ };
+ int[] init = new int[1391];
+ for (int i = 0; i < init.length; ++i) {
+ init[i] = base[i % base.length] + i;
+ }
+ Well44497a mt = new Well44497a(init);
+ int[] refInt = {
+ -1464956854, -1524360321, 986845646, -182050548, -818943186, -1744848370, 1392434650, -182648505, -2026593838, 1254866610, -410459761, -1392048371, -968730026, 1485793687, -728749746, -112685463,
+ 275126404, -1101838984, 1193096287, 443511615, -510869213, 549869992, 1974458428, -1217587840, -335835016, -2048974745, 1066947099, -611611187, 1978925459, 688164478, -463344808, 56995910,
+ 699288809, 606392470, 117418673, 1948706703, -485598135, 385841705, 1725261146, -919553921, 70643668, 2128611684, 1720197347, 738706713, 1162026860, -611442152, 1469145601, 2051653750,
+ 609067755, -1971782890, -971114565, 776260144, 1619791127, -1547233838, 1502505722, 913168193, 1761269649, 81782996, 62251540, 1519079156, 1239007000, 489633356, -800433470, -2107278046,
+ 495320431, 269446836, -2013306553, 1074614697, 1645125348, 584369930, -405429577, 1211134012, -2060113546, -2047824, -443978800, 271218497, -1002185964, 1519315874, -695096464, -79101601,
+ -1521653608, 192426133, 1159511202, -1354494985, -477280535, 583522228, -661741458, -1251175621, -369487281, -2015449518, -2102058930, -645264919, -925270025, -1674575999, 1363844609, -831732660,
+ -1668989157, -1861246633, 83763283, -1056074975, -519054258, -1546386261, 1691674654, -885968657, -1189571815, 2095154843, 1686743191, -1859471265, -261593938, 1721982136, -491120252, -949699153,
+ 642525852, -2005306625, -1004765905, 742736856, 1653443876, 788423835, 1536155740, 879514143, -1510757104, 115238646, 28600662, 1485490803, 1272460710, 523153480, -766782926, 1332478031,
+ 528775440, 302965264, -2046891123, 1108139271, 1611601128, 550846467, -439082190, 1244786747, 941120547, -35568474, 1756370964, 304870369, 1902684028, -408710726, 1673189520, 1180987663,
+ -1488131864, 158973303, 154514890, -1387953397, 1453732833, -1342263302, -628153633, 4710424, 619931109, 721411332, -2135645486, 1688696681, -891749588, -1641122924, 1397432310, -865254619,
+ -1635468227, -1827787970, -1311416657, -1022618057, 1411688086, -1579840139, -637954674, 2115653281, -1155985079, -1043532593, -374286955, -1825883832, -227940643, 1688394137, -524577925, -983222470,
+ -1955769926, 626525757, -2009760930, -1855453635, -676923169, 754966926, -291202391, -2126042921, -1477304277, -1409345382, -1264640578, -441993991, -17611930, -1576809974, 2137694350, 1299022733,
+ -762509116, -1087399293, 819303572, -14571174, -719035481, -1644675278, 1492736905, -15038081, 974773023, 1087127339, 1790024863, -1493135734, 1936273291, -442361741, 1639666948, 1147532756,
+ 174955156, -1537685747, 187972574, 275303083, 1420277149, -1375787574, 1873043153, 38164241, 653451946, 687758113, 899667071, 1722219976, 2146668333, 587401069, -26582672, 2034645447,
+ 1401801794, 1043291001, -1277898614, 2116116828, 1445274301, 150534325, 469242183, -937704471, 171074779, -204638071, 1269913689, -771734064, -12280461, -1182158859, 1704390140, -263303749,
+ -848503723, -1822849148, -634064465, 1130988864, -1515750310, -908815400, 1487214333, 994482967, 853103628, 1711185413, 1520342001, 1067859186, 1693632130, -603831333, 292236742, -800655385,
+ -1467184928, 221125007, -1697377800, 1293953144, 1730537111, 1073329737, 519625212, 689636032, 1127394154, -1496469136, -1214585810, 822152197, -1572579275, -527866383, -996792678, -2058452887,
+ -1133767559, 576275042, 1579109209, -295089371, 1502267384, -724281876, -911879875, 1131096177, 333026744, 1238706603, 1067340063, -745697708, -973992204, 1560446744, -664017057, -616056490,
+ 1099714049, 674159948, 383625825, 1411443110, 1862818263, -1896254899, 1322476914, -719144734, -1540101945, 988154902, 781856577, 2013381051, -2059071359, -142073207, 60252832, 2052050421,
+ -666391497, 376633738, 1663011481, -1706886481, -1870003191, 1003819645, 898131216, 778824906, -656875645, -1730811011, -1751653787, 2056079904, 231977636, 1831419220, -465545074, -1505266471,
+ 1034419975, -133864043, 1876779821, 1879792902, -100100435, -959264741, -472668436, 203584096, -46980157, -1478047098, -979669209, 809008494, 1279644171, 2055793632, 1385672419, -1756428826,
+ -1790481031, -2089665073, -1608595011, 457322987, 1267418945, -19541848, -796352273, -1049973752, 30940894, -539710199, -1097391703, -779353550, -1328320498, -735447662, -918513196, 1516945649,
+ 1218919237, -251287485, 1826366637, 353082340, 889839220, 399638904, -1462573609, -618450466, 1429903706, 2095548034, 1486594475, -1053248922, 74346322, -357998703, 1790710495, -146359619,
+ 1581657509, -797737661, -920778913, 608399665, 646679359, 1861775150, -1014371223, 476735306, -1577737028, 383018939, 1234592859, 344770283, -472763155, 187217985, 1245828866, 1936329359,
+ 61243025, -1979390025, 903671173, 302699505, -1677126111, -1194113496, 835857595, 706998946, 70931462, 1374113464, -1464459699, -231081598, 1366205112, 396990527, -1615015619, -968458597,
+ 457632575, 24361353, -1120685182, 2101590608, 1654666456, -1208442054, 579414359, 1078056578, 217408674, -1560683025, 815178420, 1219326466, 450032327, 774403237, 54597342, -664057229,
+ 447132403, 50603973, 435640301, -1224073863, -1339908037, 1775470944, -1378119263, -1375189988, -1287971162, 29816317, -1313418882, -1824967031, 443540716, 11064217, -1463969487, 1967601549,
+ 124474667, 1230898256, -1741455555, 561643750, 933295231, -923145874, 245538199, 289478386, 200552280, -268887021, -1598221376, 1236123270, 318325803, 773964550, 191670680, 158043961,
+ -762639146, -416703928, -721969492, 1664330785, -584949010, 1509045840, -2066001147, 1728613092, -1103375821, -1262079070, -2034789427, -418216342, -546365126, 1235751589, 1639799329, 2085089663,
+ -697590049, -2007054256, -147701903, 209371702, -1868450893, 1241065116, 1537364837, -1035970557, 318040217, 150492098, 1841159805, -491979749, -1275490577, -1759443566, -697538216, -1589624976,
+ -678703557, -189067001, 1539472677, -1396089831, 271512148, 180483983, 483714313, 703861378, 2122114992, -600097045, 522009268, 160429181, -744428886, 1541223403, -1211039718, -1167643980,
+ 1551471162, -816207368, -1429258613, 1350901561, 1934120609, -961643277, -214772286, -2128270227, -1561239720, 1493926966, 1376671007, 94966082, 221846150, -164351411, -51309876, 497148497,
+ 1233668542, 266257753, -773473851, 953946385, 420815294, -1390653175, 1834391782, 4704447, -891751440, -744104272, -1082756642, 1431640408, -1912055536, -159789461, -704946016, 1956368139,
+ 642279822, -374415338, 1562655802, -272964020, 1071498305, 667364168, -1546405154, 341389690, 1360662999, 377696332, -437020076, -1668574556, 1242655350, -756555890, 645954261, 1914624235,
+ 2134904445, -247737098, 143667521, -17668806, 1804148531, 414247300, 1030053929, -1595215075, 887532426, 553113691, 1173830167, -303724353, -280418143, -1143962122, -1898518451, 36464746,
+ 1189572700, -1549967582, 1093341440, -452303819, -731023001, 1111173883, 1678013973, -836458212, -842956392, 212774049, -845621791, 966282353, -823130040, 700410571, 619075141, -304785045,
+ -1816233676, -1789653997, -166914694, 690663021, -669570330, 1098624444, -987380984, 452844935, -1089825546, 1221160503, 1217375341, 512281644, -1106887134, 1665404458, -1462594714, -207498587,
+ -789271490, -723469709, 512055365, 1445951882, 1692473633, -996873493, 1445046730, 993087194, -1666188562, -897427329, 1008869698, 1236029718, 1499207233, 1704947182, -1815799281, 686399988,
+ -475436580, 1588892458, 884859588, -471913926, -487416631, 1323960741, -1257612174, -468909314, -1866654496, -1417895838, 1707647971, 997140465, -1358794225, 1929422460, -605622778, -1587468566,
+ 469149623, 1121515927, 748484204, 1201983830, -1906623591, 76398473, 261109068, -796025669, -1964466661, 1739898262, -756850622, 1581369453, 1484675811, 484136467, -705983890, -1357931714,
+ 548520423, 741992908, 1017931838, -2078503520, 2097871343, 569233897, -91903627, 1864053450, -876129714, 336670307, -1950420274, -872316480, -662220291, 275724295, 703565412, 1334248646,
+ -217559198, 1044090753, 743502488, -1518545318, 20614180, -768582053, 976522354, -25129962, -983639284, 71722595, -119236393, 368844119, -795808244, 696073964, 1379765302, 235083623,
+ 666280330, -1313689346, -643870520, 534522699, -250414377, -1239276164, 159264592, -1119503518, 1168161619, -1366518946, -1335653301, 248092140, 1390152547, 2051602724, -1023547981, -1479782621,
+ -1785785862, 1609789158, -919124123, 1703200068, -852553456, 1573706142, -376011685, 305068766, -1231775451, -1536883494, -125122369, -896696968, 852651567, -458154391, 747781704, 1173040469,
+ -1569200836, 312506093, -1680530410, 117086271, 794587661, -1231003908, -1048955503, 2119305423, 1636729108, -522378372, 1627421730, 545077470, -1683264872, 1486496559, -1793064672, 1908368749,
+ -1226052295, 1399248776, -588193954, -1289386125, 534647065, 2126245059, -238362987, -1244573058, -1571832269, -2052693379, 1494767731, -528668449, -980826491, -151282847, -1468523556, 1876349941,
+ -301654558, 1467960576, -741720848, -612158589, 92376910, 987915105, 1037689578, 793773489, -1387669541, 349490139, 564784004, -1161242130, 619703053, 2063233129, 190888106, 81845991,
+ -1482466066, 283234313, 114355492, -1879406787, -1283370924, -1378903370, -730141747, 1570738286, -281348873, 2131743196, 795654462, -497365688, 437612465, 1928618254, 1433118279, -1801292119,
+ -2059248836, -221673230, 163637697, -411319468, 244353317, 786753178, 489172932, 464627154, 1258915212, -229028334, -994675463, 1931657329, 1784181437, -97111947, 1728952452, -1329133577,
+ -1606156362, 1341196121, 1679632329, -796545286, -1021125869, 1427825468, -214986389, 250791528, 1029777000, 90661677, 602529506, 2068040879, 1483801763, 2332097, -457467017, 672399614,
+ 1542769193, 1781253216, -1967165705, -2052227925, -1248173215, -1676554121, 292413596, 209649573, 1750689340, 1946874730, -832845570, 1774178655, -450175610, -431901779, 613330756, 1969434259,
+ 1251099237, -1320908513, -50659188, 273178515, -296290724, 1195998469, 1329813722, 759419114, 1003396150, -274557619, -548886303, -2055397788, -766678640, -464045978, -1835907569, -169406709,
+ 820751456, 1778613303, -1582073956, -1728391771, -2075389498, -1606584632, -1702107251, -15724560, 45610235, -1967510298, -671487775, -1841110041, -913365944, 869680052, -798103472, -1564096927,
+ -918899909, -810066882, 428829752, -1413487973, -844240890, 1343914280, -689285374, 1827745702, -799686631, 1696465705, -726159000, -1381157526, 1649221296, 1791106481, -1872852642, -485685063,
+ 1534949133, -1611901907, -581776031, 242740701, -382394666, 668419384, 388297992, 748818886, 713804061, -1783774172, -1823401590, -1009098384, 2071462929, 1154475522, 1309810666, -1734475040,
+ 1212095416, 988288210, -1457428115, 1699730041, -1804729443, -1922824494, 1000076038, -226555981, 131425181, -1071582828, 357680377, 1574190179, 996651958, 965704429, -47651768, 243931978,
+ 808955117, -652323633, 544967309, -1199510217, 702795379, 997685748, 1593927308, 2119371055, 1451401230, -41992913, 2033816081, -1030495962, 1764010175, 457470691, -2001190141, -373358035,
+ -1950331268, -1291674220, 642934467, -1825725718, -1555687487, 1664472129, -24722338, 1899539596, 78519318, 1662555805, 1744711308, -2142888582, -1597853572, 118030659, 1596713428, 404304267,
+ -1350880388, 648702031, 1185458591, 1798138033, 819516445, -1466759682, -751277607, -879817426, -1931050435, 1465603177, -1402344216, 768491239, -1404853657, -1915685264, -1845859847, 313163207,
+ 1239598382, 1988767047, -555152530, -1925665864, -182399255, -1392390808, 64861291, -511875035, 1879964459, 918905020, -840773616, 459610189, -1522352470, -1821396360, 977274705, -60616465,
+ -1846727880, 1208592937, -515359427, 1127607806, -395032287, 491869604, 2053794084, 568321750, 1597027438, 1355613070, -2069482724, 1899252555, 844726247, -625112193, 1146099491, -1037855139,
+ 1203928737, 1875686061, 994108281, 1471873396, 2026801570, 4941446, -1066074241, -983738686, 2037429697, -836521112, -633388883, 1221918725, 2137035208, -369891832, 372509548, -110916409,
+ 80517712, -658056946, 727893428, -1353651002, -475459562, -291323023, 1059377566, 591801919, 1018232602, -348255729, 1863827426, 246032476, -1026132864, -1356383176, -1224690998, 262442981,
+ 1257773681, -1738604660, 77131430, -1320261233, -2342727, -1817187590, -1883997191, 1367221809, -1863623746, -1132606249, 149024763, -1228275128, -578030399, 356914163, 2109691820, -880313621
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well44497bTest.java b/src/test/java/org/apache/commons/math/random/Well44497bTest.java
new file mode 100644
index 0000000..87f8fb5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well44497bTest.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.random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Well44497bTest {
+
+ @Test
+ public void testReferenceCode() {
+ int[] base = {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000,
+ -595495729, 1047766204, 1875773301, -1637793284, 1379017098, 262792705, 191880010, -251000180,
+ -1753047622, -972355720, 90626881, 1644693418, 1503365577, 439653419, 1806361562, 1268823869
+ };
+ int[] init = new int[1391];
+ for (int i = 0; i < init.length; ++i) {
+ init[i] = base[i % base.length] + i;
+ }
+ Well44497b mt = new Well44497b(init);
+ int[] refInt = {
+ -102003638, -1254584449, 836441550, 1949705484, 653000494, 1579400718, 699652570, -140738233, 1164288466, 419933874, 366568847, 780567309, 1867405910, -350557801, -964350642, -1323492759,
+ 191502468, 398676344, 1568976991, 1005053759, 199053603, 31083944, 74697788, -1343941248, -1205631880, -1637961625, 361813531, -1706096179, -403340909, 1666226814, -2034962600, 1237102662,
+ -1833248535, 1584255126, 1295661745, -1753848945, 1208145993, 930278953, -733716134, 192752767, 1692463060, 1727273316, 2122952931, -809025255, -992081044, -895539688, -419372543, -1835478922,
+ 2089629419, 1646590742, -1261083717, -1005462992, 1619627287, -1437723182, 1619689210, 1319393089, -1816963183, -150214444, -513482220, 1897815796, -1861960936, -1766444468, 2034653890, 585657634,
+ 1867016559, 696942260, -1536237241, -527055447, -1554805020, -1063992566, 1024799415, 1782080572, -1884276362, 129028272, 1427925968, -1154222271, -1383146732, -1580532830, -1907049616, -104299169,
+ -1780913000, -2090815339, -1789809502, -1521443849, 1226625769, 1126090676, -2117094290, -449575109, -218982833, -695554478, 35923022, 1386126825, -95031305, -168657023, 436674049, -1137917876,
+ -2045134053, -1025629865, 133144659, 64226081, -1966942130, 700813483, 344058910, -910646033, -212789479, 740360859, -1269028713, 1517763679, -664178514, -683718472, -71951996, 86583727,
+ -1235669348, -1426987265, -166598353, 214190040, -1436967644, 233824411, 710927452, -1939548641, -433607408, -1075939594, -1549702826, -1310827917, -640604762, -696863672, -1282162126, -546470833,
+ -1516734192, -513809904, -458526835, 708926727, -476643096, -2108375037, -2870478, -1460116421, 436587555, -948939610, 1891375124, 1944216545, 959034236, -1267038790, -1695098736, 1853748495,
+ 1594424552, 1270099319, 1139585482, 1837434635, -709909535, -457524230, -887118113, -241703912, -1888225819, -751575804, 1122280146, 1194255209, 949350188, 892826516, -791212042, -151203035,
+ -859297731, -1979039938, 323603119, -1022065097, -1804294506, -385802891, -2127523442, -720380255, -1315859127, 999649487, 335041941, -1732821688, -1833409827, 535715225, -1285355653, 1206723386,
+ -1141619270, 759796285, -1599504546, -1988521411, 1056668895, -852564594, 1056509609, -1831687977, 754168875, -1301144422, 922880446, -1502666503, -949791898, -1043870198, -1136941938, -1649670259,
+ 1342769348, 1692605059, -132279148, -1108038310, -14355545, -1611387086, 1651826569, 877600127, 1356160799, -759125205, -1300490081, -414938486, -201479285, 1958709363, 1513313540, -1396836908,
+ 1352702612, 1142506253, 52969438, -365142357, -1619054179, -1368514102, 1470750417, -1420830959, -843909462, -1679570143, 1447444607, 234551752, -1507452115, -1433234579, -680890000, -497305145,
+ 860408898, 263376761, 1537070218, -592353956, 1587852989, 1756653749, -2081320633, -1547020311, 723771611, -883819383, 1899879513, -268417584, 1058606451, 1665941493, -1630340612, -614737477,
+ 891313237, 1368950660, -1166187089, 296322368, -1908770726, -2120378408, 1245479677, 1879710487, -1705947124, 1018371589, -1715010575, -1096078094, -1749891454, 2130888667, 318647750, 554592231,
+ -489121568, -1809605233, -1697905160, -953926536, -2013960553, -148884919, 1822739964, -1466301760, 141999978, 1946526064, 1323036718, 864390149, -2141665227, 1510602225, 1468408474, 1277039721,
+ -1368096647, 180965986, 2140852057, -688071899, 819713016, -154385940, -1182972611, 1257224305, 1392607672, 1364245931, -1768434401, 323522132, -555278604, 474186520, -1178901665, -2137343658,
+ 1636421121, 1398987084, 1276656225, 1013316006, -955917865, -1537149363, -179145358, 342862050, 1172728007, 736300054, -1114656959, -1831840325, -1882353535, -442915191, -1206488416, -1818534411,
+ 25312311, 2100297098, -1562767719, 1762553519, -1853194231, -1152612739, -2020055792, -809572150, 848584579, -535789699, 1817604709, 1343773216, -602234204, 1739930964, -833790834, 501215449,
+ -730104057, 1217783189, -681773267, -611951354, 978387629, -1516811237, 974303980, -1389665696, 2091099075, -727528826, 2116026151, 271935854, 613242379, -2100429856, 190004963, -1629612570,
+ -1362888327, 175094223, -917873219, -2008308245, -401946815, 504218792, -1966525201, 4106248, 164895454, 226502921, 655865257, -610528718, 189428750, 1055978898, 17603028, 591024369,
+ 1127922501, -1546639293, 1994174637, -724136988, -673919372, -1665002120, -612145705, -793102882, -1904763558, 757565058, -2091240021, -2123324826, -1518702766, -802889839, -223045921, -1509216579,
+ 1195556261, 2079259971, -903969953, -1781800655, 1834950463, -956531922, -1152550807, -1116052662, -348802884, -1395330117, -91758501, -19115285, 1926930669, -1015793599, 545904386, 1617858191,
+ 716963473, 1917089719, -980914811, -212646927, -1634695647, -1857924568, -1462257477, 1273750178, 1060328454, -361604424, 867932749, 451213698, 405780152, 1165233215, 1877230909, 2103114395,
+ 1644330815, 1252998537, 1961603970, -1533101488, 1790456024, -38226118, -1306744489, 713676418, -1535871838, 1378109935, -338185548, 1647669762, -477465913, 203482277, -1949756706, -503326093,
+ -638704909, 320186309, -1435581459, 907446649, -77384645, 537368928, -335347295, -1912061924, 547819174, -225549827, 1089455486, 463516297, -240127764, -85895271, 2053179697, -287394931,
+ 921878827, -933362608, -1178670275, -1200942874, -672571265, 574422382, 1441734039, -1814493454, 165751640, -176907245, -1170992192, -2123252090, -1435971541, 1591853830, -885477992, -792847559,
+ 1359875286, 1038392904, -2027255124, 687291425, -165513490, 1391146576, -1387080955, 794663652, -807189965, 667820962, -545384499, -1371368854, -689031878, 1504805541, -752825823, -1920047745,
+ -1884874017, -350566320, -197152911, -181743050, -798415949, -915922276, 1790690149, -363997181, 1923116185, -1326427198, -1621079427, -1997440997, 1798118127, -2053110382, -159879848, -1286787216,
+ 1046436411, 1832030471, -389092059, 71686169, -76025260, 1914270607, 1854169353, 872157826, -1774323792, -575165717, -1919931724, 2051498109, -1176174934, -883578901, -1253047270, -1310573900,
+ 245466682, -1784824328, -1319912821, 1377340217, 1364313761, -408687373, 142333378, -1952335763, -1703126184, 316314678, 2030555423, 488640834, -1783293306, 2116925005, -428337460, -42966447,
+ -476226114, -325172903, -1690748475, 852791569, 26490302, 85251337, -1374975770, -376283969, 982639600, 595119792, 376403422, 1574509912, -1509664496, -1901241749, -59019104, 358566667,
+ 341667214, 184541206, -550950854, -1897143732, 1595753537, -1236127928, 2014297822, -2033179270, -669806121, -1927596980, 1010735700, -581795164, 1922398838, -1456743538, -1633813803, 323177707,
+ 2002098813, -2099067658, 277393729, -671911622, -384463053, 2028267908, 367391785, 1270052637, -172839030, -650486693, -831800809, -1255138113, -137512799, 1904317942, -8229811, 707361898,
+ -276859812, 50417442, 1487081728, 1577776181, 1994451303, 1237303035, -602016235, -1905218276, -1895725672, 1172978849, 801129953, -1819485071, -587985848, -2010386741, -1645226427, -850866837,
+ 816998708, 357665811, 1955856762, 1617902189, -1013761306, 146782652, 904185608, -500146809, 2085848310, 1917713975, -1823786899, 1994184748, 789196322, 1766857258, 1770685286, 58213029,
+ -1699628994, 346827379, -1274423227, -5079670, -193099487, 1020296939, -1795904054, -1951053094, -43782418, -375403393, 1026761026, -207269610, 1364563521, 1578793454, 457809423, -534138380,
+ -1052938788, -1897101526, 1449976516, 2052800058, -1145169719, 1476303269, 370625650, -325249282, 2165984, 1631432802, 1032336355, -1292978191, -1810172401, 725725820, -1162678778, 702624490,
+ 1387673527, 981825943, -556408212, -1108487850, -1782136935, 1582316425, -1752682164, 307235003, 1386486299, -1343636074, 1936875586, -1265279891, -345847069, 928241171, 239050350, 1859358526,
+ -664776217, -823267892, 346651710, -867656288, -1907921425, 1362445801, 541145461, -192727350, 1649366606, 244694627, -488180018, 214008256, 2032125437, -1678591993, -264593820, 1309017286,
+ -652451998, 1845366657, -703990120, -550186406, -630285276, 1782372955, 1650564210, -1127904234, -1407983860, -1119422877, -1565976361, -1913545385, 549841420, -1410323732, -1964467146, 228296551,
+ -421026422, 1929094398, -266906424, 264810315, -2008122665, -1088278148, 141242192, 1871293282, 234634067, 1724159838, 1638271051, -837713428, -657941661, 168093988, 708605363, -1881612509,
+ -1810496006, -193495322, 1889982309, -2050702012, -693694192, -1249780322, 718733403, -76349730, -188217051, 920912090, -1814078273, 2013358456, -1948845521, -198407575, -1248904632, 1182772565,
+ 1236791612, -1297489171, -1958468586, 1437011007, 390460941, 113068796, 1247982993, 2102889679, -1563524844, -128174212, -754095070, -1461699362, 943615640, -1013270737, 221253920, 1514140013,
+ 1596946745, 674222984, 616356702, 1811224435, -1764322023, -1653707581, -1702404459, 390678142, -209506765, -1398278531, -117061517, 1625861343, 659048069, -1490678943, 846536668, 210715637,
+ 1855458786, 1727745280, 1086729456, 1109111683, -985298098, -1813777567, -954599702, -1522494031, 1166103515, -191868965, -1048777852, -661271058, 1161457421, 1509090409, -919753558, -155431193,
+ -1774302994, -366390263, 2090138916, -693431491, -1693888428, 1846774454, 925855693, 474383470, 208889079, 382195164, -283005634, -2095134392, 579927985, 1390765326, -1766119865, 900457129,
+ -1503703236, 974952690, -107714111, 381338452, 1187256613, -860560742, 524103620, 1499506130, 197755276, -790802926, -406920967, -1972219791, -665721155, -113336203, 1037154436, -1185441801,
+ -745541706, -546274471, 1988928457, -1975403782, -1167172845, 777779004, -1560935061, -140258712, -1243598232, -1394149587, -785002782, 311842991, -1025469277, -605350463, -1251538057, 537203966,
+ 597777961, -1845767072, -1556349193, -1491015509, -1935936671, 2093498487, 1908270236, -315396187, 1356362300, -2025658518, 630119678, 276190559, 510123398, -1266145363, -170152124, -151540077,
+ -477900187, 1895894303, 1870333068, -1169891437, 353366620, 2111175941, 1691245786, 1318765802, -90993610, 921309517, 118241505, 367005284, 1624861072, 2010785894, 865255951, 1717799691,
+ -80757664, -644944841, 136999836, -341686875, -1908076090, -1968934200, -346397811, -184213520, -511811333, -2118173466, -1086490399, 1795322855, -635494328, 415716276, 851044432, -904636831,
+ -1972230341, -64337858, 571177016, 1248814747, -1351030778, 457872680, 1843549954, 1718960038, 815088665, 1812961065, 360686952, -1356586646, 1657802416, 1776192945, -786723490, -342254407,
+ -236653811, 771014701, 906386785, -308057635, 1907957462, 206000440, -42143480, 900403654, -917549795, -310520796, -1713627766, 2061136240, -377977839, 891282946, -821163030, 328143584,
+ 1503793080, 551621842, -2086273683, -2070526343, 91195293, -1654389038, -1035734266, -336619597, -1220221027, -1468468844, 2105626873, -841372573, -122707018, -2013073683, 494461000, -2054807734,
+ -67946259, 1914163407, 1941835405, -1027244745, -768123277, 419129844, -275750260, -171533009, 97756174, -17651409, -1578102255, 995291430, -1587462977, 692904675, 951632643, 1882101293,
+ -1546298756, 2018418068, -1790777661, 1542305514, -1437624383, 469587009, -1647853474, -1318279028, 497228822, 726733469, 1693133452, -2091185798, -209017732, 126386499, 1056958932, -2105494133,
+ 754067324, 96463951, 83701151, 1101658289, 1485852701, 553783806, 1898769881, -1072031442, 1438062141, 1992540265, 1152252136, 1019391719, -175951257, -6691216, 989789689, 968359367,
+ -1330392786, 1704963399, -998432914, -948060232, -1921688855, -975840920, 1360273515, -872810459, 12676907, -1908050756, 883609616, 65641549, -200365398, 1386653304, -1203665071, 1878689007,
+ 426262328, 315375145, 1900325181, 703658494, -765404895, 1070155172, 1399748900, -804264234, -1619419026, 1347225486, 230635292, 1093717835, 14020583, -2107039873, -968325341, -1679158691,
+ 1959784097, 1065690797, 1090615161, 1311445364, 865835426, 870016646, 574122879, 1842697922, -1289210431, -1914001560, 1672467629, -900366331, -1524066872, 136503816, -1910431892, -1431958329,
+ -830367152, -1316233970, -801974860, 1560669382, -81784810, 401822577, -949103202, 943897151, -722666726, -96825841, -1092898846, 230567004, -70355840, -1398069192, -312953142, 1475420133,
+ -622491023, 1661205388, -19071322, 6024591, 1473041593, 2053897978, -1346768903, 1484764721, -1552461890, 1287146711, 1613069307, 902497864, -1504480063, 375292915, -836353108, 2047602411
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/random/Well512aTest.java b/src/test/java/org/apache/commons/math/random/Well512aTest.java
new file mode 100644
index 0000000..37c5448
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/random/Well512aTest.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.random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Well512aTest {
+
+ @Test
+ public void testReferenceCode() {
+ Well512a mt = new Well512a(new int[] {
+ 740849862, 1202665156, -199039369, -259008301, -291878969, -1164428990, -1565918811, 491009864,
+ -1883086670, 1383450241, 1244617256, 689006653, -1576746370, -1307940314, 1421489086, 1742094000
+ });
+ int[] refInt = {
+ 1634813289, 1876773016, -973836208, -2130023652, -1045460084, -1834384857, 1691032973, 609714289,
+ 2033920362, 555915483, 6680992, 1958127415, 1866469645, -1471336965, 2049178762, -192324811,
+ -2056050066, 810879705, 1405046309, -781317118, 1012782311, -1045081032, 728377508, 1473511660,
+ 290489070, 326666761, 2018299979, -1876688058, 1239968501, 1464625040, 2025151042, -101397407,
+ 1387902041, 210959839, 1366359326, -476473433, 153180037, -1607631523, -506743495, 17888738,
+ 313865008, -340504498, 586684079, 1243699375, 753162229, -646761694, -739189655, -210120185,
+ -1856358726, -628255542, -1812798197, 1416288088, 1077967722, -846846208, 1379850409, -580183344,
+ -1858959, 210859778, 295841424, 1492774865, -1415543680, -344870570, -1942779197, 1549510646,
+ -389544849, 314254218, 11784988, -1311757368, 1719514841, -764610517, 1296788970, -994707050,
+ 783854563, 422654144, 387639079, 1219688425, 2144352572, -834212874, -1036550358, 935909479,
+ -568610842, 1327498837, -588933178, 1910065754, -40851599, -182063170, 1302731458, 541311559,
+ -1647345522, 805224371, -1721196679, 1518507830, -952689880, -433276260, 509675254, -777259954,
+ 1277810106, 284054896, 936042202, 2036836351, 1956412426, -1186403024, 287795400, 2135311211,
+ 720485927, 1500695024, -281656583, -1277937322, -1628968482, 1242814831, -2030700974, 1473867890,
+ 440813549, -1357033971, 28384076, 1602731216, -641465746, -609054347, 635938444, 1472898176,
+ 1476894555, -747974186, -1590337055, -884242108, -389736197, -2066984505, 1087103272, -1236446290,
+ 31657463, 1835715432, -468439078, -2132633204, -434609235, 258308151, 1851926761, -1630139159,
+ -1344617241, 1969204215, 619463174, -174392624, 207475487, -1619828078, 1327980298, -83968178,
+ 445951782, -1786230541, 6279288, -580982231, 1550645552, 2006533941, 275746007, 455676647,
+ 2019637349, 1115547704, -1313120106, -516213449, 73752461, -1382448112, 398589620, 1319888048,
+ -1595572334, 1566934536, -1735685764, -1509545339, 1458173912, -549395819, -618827040, 1516624531,
+ 1900757187, -1454200688, 965524719, 488355065, -1869294316, -810641680, -2059428251, 1454656431,
+ 1329120541, -232185900, -994996943, 1855980910, -452077812, 1565630611, 759842266, 1241435187,
+ -1390456063, 1946400597, -2032319771, 683667881, 905911106, 1983310786, 120010546, 526018017,
+ -1946881912, 205004987, -1307250612, 2130980818, 2052864161, 189839787, 1789478047, 406168885,
+ -1145186347, 8507675, 1277188815, 1492619042, 2009819675, -1627411598, -851016743, -1828234956,
+ 1962622506, 2140398255, 236935165, -337237772, 1263419111, 516775236, -335741025, 1391328225,
+ 455979249, -1457534664, -657606241, 485648133, 1762116343, 1194889600, 817834937, 321150162,
+ 131159182, 290277758, -1876924740, -1770401129, 1291602973, -1003642974, -1580211929, 1520422021,
+ -399171579, -24315308, 453805396, -659197747, -205656847, 466526550, 1444397201, 1178091401,
+ -1157268826, -602394028, -1370668795, 1614896435, 1699071659, 1864753793, 1888518358, -1721244514,
+ 1812776767, 668822227, -297283057, 2130183333, -1169618692, 912860240, -2028253096, 1244694278
+ };
+
+ for (int i = 0; i < refInt.length; ++i) {
+ Assert.assertEquals(refInt[i], mt.nextInt());
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/special/BetaTest.java b/src/test/java/org/apache/commons/math/special/BetaTest.java
new file mode 100644
index 0000000..76642bc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/special/BetaTest.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.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.TestUtils;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class BetaTest extends TestCase {
+ /**
+ * Constructor for BetaTest.
+ * @param name
+ */
+ public BetaTest(String name) {
+ super(name);
+ }
+
+ private void testRegularizedBeta(double expected, double x, double a,
+ double b)
+ {
+ try {
+ double actual = Beta.regularizedBeta(x, a, b);
+ TestUtils.assertEquals(expected, actual, 10e-15);
+ } catch(MathException ex){
+ fail(ex.getMessage());
+ }
+ }
+
+ private void testLogBeta(double expected, double a, double b) {
+ double actual = Beta.logBeta(a, b);
+ TestUtils.assertEquals(expected, actual, 10e-15);
+ }
+
+ public void testRegularizedBetaNanPositivePositive() {
+ testRegularizedBeta(Double.NaN, Double.NaN, 1.0, 1.0);
+ }
+
+ public void testRegularizedBetaPositiveNanPositive() {
+ testRegularizedBeta(Double.NaN, 0.5, Double.NaN, 1.0);
+ }
+
+ public void testRegularizedBetaPositivePositiveNan() {
+ testRegularizedBeta(Double.NaN, 0.5, 1.0, Double.NaN);
+ }
+
+ public void testRegularizedBetaNegativePositivePositive() {
+ testRegularizedBeta(Double.NaN, -0.5, 1.0, 2.0);
+ }
+
+ public void testRegularizedBetaPositiveNegativePositive() {
+ testRegularizedBeta(Double.NaN, 0.5, -1.0, 2.0);
+ }
+
+ public void testRegularizedBetaPositivePositiveNegative() {
+ testRegularizedBeta(Double.NaN, 0.5, 1.0, -2.0);
+ }
+
+ public void testRegularizedBetaZeroPositivePositive() {
+ testRegularizedBeta(0.0, 0.0, 1.0, 2.0);
+ }
+
+ public void testRegularizedBetaPositiveZeroPositive() {
+ testRegularizedBeta(Double.NaN, 0.5, 0.0, 2.0);
+ }
+
+ public void testRegularizedBetaPositivePositiveZero() {
+ testRegularizedBeta(Double.NaN, 0.5, 1.0, 0.0);
+ }
+
+ public void testRegularizedBetaPositivePositivePositive() {
+ testRegularizedBeta(0.75, 0.5, 1.0, 2.0);
+ }
+
+ public void testLogBetaNanPositive() {
+ testLogBeta(Double.NaN, Double.NaN, 2.0);
+ }
+
+ public void testLogBetaPositiveNan() {
+ testLogBeta(Double.NaN, 1.0, Double.NaN);
+ }
+
+ public void testLogBetaNegativePositive() {
+ testLogBeta(Double.NaN, -1.0, 2.0);
+ }
+
+ public void testLogBetaPositiveNegative() {
+ testLogBeta(Double.NaN, 1.0, -2.0);
+ }
+
+ public void testLogBetaZeroPositive() {
+ testLogBeta(Double.NaN, 0.0, 2.0);
+ }
+
+ public void testLogBetaPositiveZero() {
+ testLogBeta(Double.NaN, 1.0, 0.0);
+ }
+
+ public void testLogBetaPositivePositive() {
+ testLogBeta(-0.693147180559945, 1.0, 2.0);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/special/ErfTest.java b/src/test/java/org/apache/commons/math/special/ErfTest.java
new file mode 100644
index 0000000..cdde1b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/special/ErfTest.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.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 1054184 $ $Date: 2011-01-01 02:23:02 +0100 (sam. 01 janv. 2011) $
+ */
+public class ErfTest extends TestCase {
+
+ public void testErf0() throws MathException {
+ double actual = Erf.erf(0.0);
+ double expected = 0.0;
+ assertEquals(expected, actual, 1.0e-15);
+ assertEquals(1 - expected, Erf.erfc(0.0), 1.0e-15);
+ }
+
+ public void testErf1960() throws MathException {
+ double x = 1.960 / FastMath.sqrt(2.0);
+ double actual = Erf.erf(x);
+ double expected = 0.95;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(x), 1.0e-15);
+
+ actual = Erf.erf(-x);
+ expected = -expected;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(-x), 1.0e-15);
+ }
+
+ public void testErf2576() throws MathException {
+ double x = 2.576 / FastMath.sqrt(2.0);
+ double actual = Erf.erf(x);
+ double expected = 0.99;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(x), 1e-15);
+
+ actual = Erf.erf(-x);
+ expected = -expected;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(-x), 1.0e-15);
+ }
+
+ public void testErf2807() throws MathException {
+ double x = 2.807 / FastMath.sqrt(2.0);
+ double actual = Erf.erf(x);
+ double expected = 0.995;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(x), 1.0e-15);
+
+ actual = Erf.erf(-x);
+ expected = -expected;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - actual, Erf.erfc(-x), 1.0e-15);
+ }
+
+ public void testErf3291() throws MathException {
+ double x = 3.291 / FastMath.sqrt(2.0);
+ double actual = Erf.erf(x);
+ double expected = 0.999;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - expected, Erf.erfc(x), 1.0e-5);
+
+ actual = Erf.erf(-x);
+ expected = -expected;
+ assertEquals(expected, actual, 1.0e-5);
+ assertEquals(1 - expected, Erf.erfc(-x), 1.0e-5);
+ }
+
+ /**
+ * MATH-301, MATH-456
+ */
+ public void testLargeValues() throws Exception {
+ for (int i = 1; i < 200; i*=10) {
+ double result = Erf.erf(i);
+ assertFalse(Double.isNaN(result));
+ assertTrue(result > 0 && result <= 1);
+ result = Erf.erf(-i);
+ assertFalse(Double.isNaN(result));
+ assertTrue(result >= -1 && result < 0);
+ result = Erf.erfc(i);
+ assertFalse(Double.isNaN(result));
+ assertTrue(result >= 0 && result < 1);
+ result = Erf.erfc(-i);
+ assertFalse(Double.isNaN(result));
+ assertTrue(result >= 1 && result <= 2);
+ }
+ assertEquals(-1, Erf.erf(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(1, Erf.erf(Double.POSITIVE_INFINITY), 0);
+ assertEquals(2, Erf.erfc(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(0, Erf.erfc(Double.POSITIVE_INFINITY), 0);
+ }
+
+ /**
+ * Compare Erf.erf against reference values computed using GCC 4.2.1 (Apple OSX packaged version)
+ * erfl (extended precision erf).
+ */
+ public void testErfGnu() throws Exception {
+ final double tol = 1E-15;
+ final double[] gnuValues = new double[] {-1, -1, -1, -1, -1,
+ -1, -1, -1, -0.99999999999999997848,
+ -0.99999999999999264217, -0.99999999999846254017, -0.99999999980338395581, -0.99999998458274209971,
+ -0.9999992569016276586, -0.99997790950300141459, -0.99959304798255504108, -0.99532226501895273415,
+ -0.96610514647531072711, -0.84270079294971486948, -0.52049987781304653809, 0,
+ 0.52049987781304653809, 0.84270079294971486948, 0.96610514647531072711, 0.99532226501895273415,
+ 0.99959304798255504108, 0.99997790950300141459, 0.9999992569016276586, 0.99999998458274209971,
+ 0.99999999980338395581, 0.99999999999846254017, 0.99999999999999264217, 0.99999999999999997848,
+ 1, 1, 1, 1,
+ 1, 1, 1, 1};
+ double x = -10d;
+ for (int i = 0; i < 41; i++) {
+ assertEquals(gnuValues[i], Erf.erf(x), tol);
+ x += 0.5d;
+ }
+ }
+
+ /**
+ * Compare Erf.erfc against reference values computed using GCC 4.2.1 (Apple OSX packaged version)
+ * erfcl (extended precision erfc).
+ */
+ public void testErfcGnu() throws Exception {
+ final double tol = 1E-15;
+ final double[] gnuValues = new double[] { 2, 2, 2, 2, 2,
+ 2, 2, 2, 1.9999999999999999785,
+ 1.9999999999999926422, 1.9999999999984625402, 1.9999999998033839558, 1.9999999845827420998,
+ 1.9999992569016276586, 1.9999779095030014146, 1.9995930479825550411, 1.9953222650189527342,
+ 1.9661051464753107271, 1.8427007929497148695, 1.5204998778130465381, 1,
+ 0.47950012218695346194, 0.15729920705028513051, 0.033894853524689272893, 0.0046777349810472658333,
+ 0.00040695201744495893941, 2.2090496998585441366E-05, 7.4309837234141274516E-07, 1.5417257900280018858E-08,
+ 1.966160441542887477E-10, 1.5374597944280348501E-12, 7.3578479179743980661E-15, 2.1519736712498913103E-17,
+ 3.8421483271206474691E-20, 4.1838256077794144006E-23, 2.7766493860305691016E-26, 1.1224297172982927079E-29,
+ 2.7623240713337714448E-33, 4.1370317465138102353E-37, 3.7692144856548799402E-41, 2.0884875837625447567E-45};
+ double x = -10d;
+ for (int i = 0; i < 41; i++) {
+ assertEquals(gnuValues[i], Erf.erfc(x), tol);
+ x += 0.5d;
+ }
+ }
+
+ /**
+ * Tests erfc against reference data computed using Maple reported in Marsaglia, G,,
+ * "Evaluating the Normal Distribution," Journal of Statistical Software, July, 2004.
+ * http//www.jstatsoft.org/v11/a05/paper
+ */
+ public void testErfcMaple() throws Exception {
+ double[][] ref = new double[][]
+ {{0.1, 4.60172162722971e-01},
+ {1.2, 1.15069670221708e-01},
+ {2.3, 1.07241100216758e-02},
+ {3.4, 3.36929265676881e-04},
+ {4.5, 3.39767312473006e-06},
+ {5.6, 1.07175902583109e-08},
+ {6.7, 1.04209769879652e-11},
+ {7.8, 3.09535877195870e-15},
+ {8.9, 2.79233437493966e-19},
+ {10.0, 7.61985302416053e-24},
+ {11.1, 6.27219439321703e-29},
+ {12.2, 1.55411978638959e-34},
+ {13.3, 1.15734162836904e-40},
+ {14.4, 2.58717592540226e-47},
+ {15.5, 1.73446079179387e-54},
+ {16.6, 3.48454651995041e-62}
+ };
+ for (int i = 0; i < 15; i++) {
+ final double result = 0.5*Erf.erfc(ref[i][0]/Math.sqrt(2));
+ assertEquals(ref[i][1], result, 1E-15);
+ TestUtils.assertRelativelyEquals(ref[i][1], result, 1E-13);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/special/GammaTest.java b/src/test/java/org/apache/commons/math/special/GammaTest.java
new file mode 100644
index 0000000..3a6d769
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/special/GammaTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.special;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class GammaTest extends TestCase {
+
+ public GammaTest(String name) {
+ super(name);
+ }
+
+ private void testRegularizedGamma(double expected, double a, double x) {
+ try {
+ double actualP = Gamma.regularizedGammaP(a, x);
+ double actualQ = Gamma.regularizedGammaQ(a, x);
+ TestUtils.assertEquals(expected, actualP, 10e-15);
+ TestUtils.assertEquals(actualP, 1.0 - actualQ, 10e-15);
+ } catch(MathException ex){
+ fail(ex.getMessage());
+ }
+ }
+
+ private void testLogGamma(double expected, double x) {
+ double actual = Gamma.logGamma(x);
+ TestUtils.assertEquals(expected, actual, 10e-15);
+ }
+
+ public void testRegularizedGammaNanPositive() {
+ testRegularizedGamma(Double.NaN, Double.NaN, 1.0);
+ }
+
+ public void testRegularizedGammaPositiveNan() {
+ testRegularizedGamma(Double.NaN, 1.0, Double.NaN);
+ }
+
+ public void testRegularizedGammaNegativePositive() {
+ testRegularizedGamma(Double.NaN, -1.5, 1.0);
+ }
+
+ public void testRegularizedGammaPositiveNegative() {
+ testRegularizedGamma(Double.NaN, 1.0, -1.0);
+ }
+
+ public void testRegularizedGammaZeroPositive() {
+ testRegularizedGamma(Double.NaN, 0.0, 1.0);
+ }
+
+ public void testRegularizedGammaPositiveZero() {
+ testRegularizedGamma(0.0, 1.0, 0.0);
+ }
+
+ public void testRegularizedGammaPositivePositive() {
+ testRegularizedGamma(0.632120558828558, 1.0, 1.0);
+ }
+
+ public void testLogGammaNan() {
+ testLogGamma(Double.NaN, Double.NaN);
+ }
+
+ public void testLogGammaNegative() {
+ testLogGamma(Double.NaN, -1.0);
+ }
+
+ public void testLogGammaZero() {
+ testLogGamma(Double.NaN, 0.0);
+ }
+
+ public void testLogGammaPositive() {
+ testLogGamma(0.6931471805599457, 3.0);
+ }
+
+ public void testDigammaLargeArgs() {
+ double eps = 1e-8;
+ assertEquals(4.6001618527380874002, Gamma.digamma(100), eps);
+ assertEquals(3.9019896734278921970, Gamma.digamma(50), eps);
+ assertEquals(2.9705239922421490509, Gamma.digamma(20), eps);
+ assertEquals(2.9958363947076465821, Gamma.digamma(20.5), eps);
+ assertEquals(2.2622143570941481605, Gamma.digamma(10.1), eps);
+ assertEquals(2.1168588189004379233, Gamma.digamma(8.8), eps);
+ assertEquals(1.8727843350984671394, Gamma.digamma(7), eps);
+ assertEquals(0.42278433509846713939, Gamma.digamma(2), eps);
+ assertEquals(-100.56088545786867450, Gamma.digamma(0.01), eps);
+ assertEquals(-4.0390398965921882955, Gamma.digamma(-0.8), eps);
+ assertEquals(4.2003210041401844726, Gamma.digamma(-6.3), eps);
+ }
+
+ public void testDigammaSmallArgs() {
+ // values for negative powers of 10 from 1 to 30 as computed by webMathematica with 20 digits
+ // see functions.wolfram.com
+ double[] expected = {-10.423754940411076795, -100.56088545786867450, -1000.5755719318103005,
+ -10000.577051183514335, -100000.57719921568107, -1.0000005772140199687e6, -1.0000000577215500408e7,
+ -1.0000000057721564845e8, -1.0000000005772156633e9, -1.0000000000577215665e10, -1.0000000000057721566e11,
+ -1.0000000000005772157e12, -1.0000000000000577216e13, -1.0000000000000057722e14, -1.0000000000000005772e15, -1e+16,
+ -1e+17, -1e+18, -1e+19, -1e+20, -1e+21, -1e+22, -1e+23, -1e+24, -1e+25, -1e+26,
+ -1e+27, -1e+28, -1e+29, -1e+30};
+ for (double n = 1; n < 30; n++) {
+ checkRelativeError(String.format("Test %.0f: ", n), expected[(int) (n - 1)], Gamma.digamma(FastMath.pow(10.0, -n)), 1e-8);
+ }
+ }
+
+ public void testTrigamma() {
+ double eps = 1e-8;
+ // computed using webMathematica. For example, to compute trigamma($i) = Polygamma(1, $i), use
+ //
+ // http://functions.wolfram.com/webMathematica/Evaluated.jsp?name=PolyGamma2&plottype=0&vars={%221%22,%22$i%22}&digits=20
+ double[] data = {
+ 1e-4, 1.0000000164469368793e8,
+ 1e-3, 1.0000016425331958690e6,
+ 1e-2, 10001.621213528313220,
+ 1e-1, 101.43329915079275882,
+ 1, 1.6449340668482264365,
+ 2, 0.64493406684822643647,
+ 3, 0.39493406684822643647,
+ 4, 0.28382295573711532536,
+ 5, 0.22132295573711532536,
+ 10, 0.10516633568168574612,
+ 20, 0.051270822935203119832,
+ 50, 0.020201333226697125806,
+ 100, 0.010050166663333571395
+ };
+ for (int i = data.length - 2; i >= 0; i -= 2) {
+ assertEquals(String.format("trigamma %.0f", data[i]), data[i + 1], Gamma.trigamma(data[i]), eps);
+ }
+ }
+
+ private void checkRelativeError(String msg, double expected, double actual, double tolerance) {
+ assertEquals(msg, expected, actual, FastMath.abs(tolerance * actual));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/CertifiedDataTest.java b/src/test/java/org/apache/commons/math/stat/CertifiedDataTest.java
new file mode 100644
index 0000000..749f6cc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/CertifiedDataTest.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;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+
+/**
+ * Certified data test cases.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class CertifiedDataTest extends TestCase {
+
+ protected double mean = Double.NaN;
+
+ protected double std = Double.NaN;
+
+ /**
+ * Certified Data Test Constructor
+ * @param name
+ */
+ public CertifiedDataTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test SummaryStatistics - implementations that do not store the data
+ * and use single pass algorithms to compute statistics
+ */
+ public void testSummaryStatistics() throws Exception {
+ SummaryStatistics u = new SummaryStatistics();
+ loadStats("data/PiDigits.txt", u);
+ assertEquals("PiDigits: std", std, u.getStandardDeviation(), 1E-13);
+ assertEquals("PiDigits: mean", mean, u.getMean(), 1E-13);
+
+ loadStats("data/Mavro.txt", u);
+ assertEquals("Mavro: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("Mavro: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/Michelso.txt", u);
+ assertEquals("Michelso: std", std, u.getStandardDeviation(), 1E-13);
+ assertEquals("Michelso: mean", mean, u.getMean(), 1E-13);
+
+ loadStats("data/NumAcc1.txt", u);
+ assertEquals("NumAcc1: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("NumAcc1: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/NumAcc2.txt", u);
+ assertEquals("NumAcc2: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("NumAcc2: mean", mean, u.getMean(), 1E-14);
+ }
+
+ /**
+ * Test DescriptiveStatistics - implementations that store full array of
+ * values and execute multi-pass algorithms
+ */
+ public void testDescriptiveStatistics() throws Exception {
+
+ DescriptiveStatistics u = new DescriptiveStatistics();
+
+ loadStats("data/PiDigits.txt", u);
+ assertEquals("PiDigits: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("PiDigits: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/Mavro.txt", u);
+ assertEquals("Mavro: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("Mavro: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/Michelso.txt", u);
+ assertEquals("Michelso: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("Michelso: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/NumAcc1.txt", u);
+ assertEquals("NumAcc1: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("NumAcc1: mean", mean, u.getMean(), 1E-14);
+
+ loadStats("data/NumAcc2.txt", u);
+ assertEquals("NumAcc2: std", std, u.getStandardDeviation(), 1E-14);
+ assertEquals("NumAcc2: mean", mean, u.getMean(), 1E-14);
+ }
+
+ /**
+ * loads a DescriptiveStatistics off of a test file
+ * @param file
+ * @param statistical summary
+ */
+ private void loadStats(String resource, Object u) throws Exception {
+
+ DescriptiveStatistics d = null;
+ SummaryStatistics s = null;
+ if (u instanceof DescriptiveStatistics) {
+ d = (DescriptiveStatistics) u;
+ } else {
+ s = (SummaryStatistics) u;
+ }
+ u.getClass().getDeclaredMethod(
+ "clear", new Class[]{}).invoke(u, new Object[]{});
+ mean = Double.NaN;
+ std = Double.NaN;
+
+ BufferedReader in =
+ new BufferedReader(
+ new InputStreamReader(
+ CertifiedDataTest.class.getResourceAsStream(resource)));
+
+ String line = null;
+
+ for (int j = 0; j < 60; j++) {
+ line = in.readLine();
+ if (j == 40) {
+ mean =
+ Double.parseDouble(
+ line.substring(line.lastIndexOf(":") + 1).trim());
+ }
+ if (j == 41) {
+ std =
+ Double.parseDouble(
+ line.substring(line.lastIndexOf(":") + 1).trim());
+ }
+ }
+
+ line = in.readLine();
+
+ while (line != null) {
+ if (d != null) {
+ d.addValue(Double.parseDouble(line.trim()));
+ } else {
+ s.addValue(Double.parseDouble(line.trim()));
+ }
+ line = in.readLine();
+ }
+
+ in.close();
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/FrequencyTest.java b/src/test/java/org/apache/commons/math/stat/FrequencyTest.java
new file mode 100644
index 0000000..4f49acc
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/FrequencyTest.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+
+/**
+ * Test cases for the {@link Frequency} class.
+ *
+ * @version $Revision: 1044981 $ $Date: 2010-12-13 01:43:57 +0100 (lun. 13 déc. 2010) $
+ */
+
+public final class FrequencyTest extends TestCase {
+ private long oneL = 1;
+ private long twoL = 2;
+ private long threeL = 3;
+ private int oneI = 1;
+ private int twoI = 2;
+ private int threeI=3;
+ private double tolerance = 10E-15;
+ private Frequency f = null;
+
+ public FrequencyTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ f = new Frequency();
+ }
+
+ /** test freq counts */
+ @SuppressWarnings("deprecation")
+ public void testCounts() {
+ assertEquals("total count",0,f.getSumFreq());
+ f.addValue(oneL);
+ f.addValue(twoL);
+ f.addValue(1);
+ f.addValue(oneI);
+ assertEquals("one frequency count",3,f.getCount(1));
+ assertEquals("two frequency count",1,f.getCount(2));
+ assertEquals("three frequency count",0,f.getCount(3));
+ assertEquals("total count",4,f.getSumFreq());
+ assertEquals("zero cumulative frequency", 0, f.getCumFreq(0));
+ assertEquals("one cumulative frequency", 3, f.getCumFreq(1));
+ assertEquals("two cumulative frequency", 4, f.getCumFreq(2));
+ assertEquals("Integer argument cum freq",4, f.getCumFreq(Integer.valueOf(2)));
+ assertEquals("five cumulative frequency", 4, f.getCumFreq(5));
+ assertEquals("foo cumulative frequency", 0, f.getCumFreq("foo"));
+
+ f.clear();
+ assertEquals("total count",0,f.getSumFreq());
+
+ // userguide examples -------------------------------------------------------------------
+ f.addValue("one");
+ f.addValue("One");
+ f.addValue("oNe");
+ f.addValue("Z");
+ assertEquals("one cumulative frequency", 1 , f.getCount("one"));
+ assertEquals("Z cumulative pct", 0.5, f.getCumPct("Z"), tolerance);
+ assertEquals("z cumulative pct", 1.0, f.getCumPct("z"), tolerance);
+ assertEquals("Ot cumulative pct", 0.25, f.getCumPct("Ot"), tolerance);
+ f.clear();
+
+ f = null;
+ Frequency f = new Frequency();
+ f.addValue(1);
+ f.addValue(Integer.valueOf(1));
+ f.addValue(Long.valueOf(1));
+ f.addValue(2);
+ f.addValue(Integer.valueOf(-1));
+ assertEquals("1 count", 3, f.getCount(1));
+ assertEquals("1 count", 3, f.getCount(Integer.valueOf(1)));
+ assertEquals("0 cum pct", 0.2, f.getCumPct(0), tolerance);
+ assertEquals("1 pct", 0.6, f.getPct(Integer.valueOf(1)), tolerance);
+ assertEquals("-2 cum pct", 0, f.getCumPct(-2), tolerance);
+ assertEquals("10 cum pct", 1, f.getCumPct(10), tolerance);
+
+ f = null;
+ f = new Frequency(String.CASE_INSENSITIVE_ORDER);
+ f.addValue("one");
+ f.addValue("One");
+ f.addValue("oNe");
+ f.addValue("Z");
+ assertEquals("one count", 3 , f.getCount("one"));
+ assertEquals("Z cumulative pct -- case insensitive", 1 , f.getCumPct("Z"), tolerance);
+ assertEquals("z cumulative pct -- case insensitive", 1 , f.getCumPct("z"), tolerance);
+
+ f = null;
+ f = new Frequency();
+ assertEquals(0L, f.getCount('a'));
+ assertEquals(0L, f.getCumFreq('b'));
+ TestUtils.assertEquals(Double.NaN, f.getPct('a'), 0.0);
+ TestUtils.assertEquals(Double.NaN, f.getCumPct('b'), 0.0);
+ f.addValue('a');
+ f.addValue('b');
+ f.addValue('c');
+ f.addValue('d');
+ assertEquals(1L, f.getCount('a'));
+ assertEquals(2L, f.getCumFreq('b'));
+ assertEquals(0.25, f.getPct('a'), 0.0);
+ assertEquals(0.5, f.getCumPct('b'), 0.0);
+ assertEquals(1.0, f.getCumPct('e'), 0.0);
+ }
+
+ /** test pcts */
+ @SuppressWarnings("deprecation")
+ public void testPcts() {
+ f.addValue(oneL);
+ f.addValue(twoL);
+ f.addValue(oneI);
+ f.addValue(twoI);
+ f.addValue(threeL);
+ f.addValue(threeL);
+ f.addValue(3);
+ f.addValue(threeI);
+ assertEquals("one pct",0.25,f.getPct(1),tolerance);
+ assertEquals("two pct",0.25,f.getPct(Long.valueOf(2)),tolerance);
+ assertEquals("three pct",0.5,f.getPct(threeL),tolerance);
+ // MATH-329
+ assertEquals("three (Object) pct",0.5,f.getPct((Object) (Integer.valueOf(3))),tolerance);
+ assertEquals("five pct",0,f.getPct(5),tolerance);
+ assertEquals("foo pct",0,f.getPct("foo"),tolerance);
+ assertEquals("one cum pct",0.25,f.getCumPct(1),tolerance);
+ assertEquals("two cum pct",0.50,f.getCumPct(Long.valueOf(2)),tolerance);
+ assertEquals("Integer argument",0.50,f.getCumPct(Integer.valueOf(2)),tolerance);
+ assertEquals("three cum pct",1.0,f.getCumPct(threeL),tolerance);
+ assertEquals("five cum pct",1.0,f.getCumPct(5),tolerance);
+ assertEquals("zero cum pct",0.0,f.getCumPct(0),tolerance);
+ assertEquals("foo cum pct",0,f.getCumPct("foo"),tolerance);
+ }
+
+ /** test adding incomparable values */
+ @SuppressWarnings("deprecation")
+ public void testAdd() {
+ char aChar = 'a';
+ char bChar = 'b';
+ String aString = "a";
+ f.addValue(aChar);
+ f.addValue(bChar);
+ try {
+ f.addValue(aString);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ f.addValue(2);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ assertEquals("a pct",0.5,f.getPct(aChar),tolerance);
+ assertEquals("b cum pct",1.0,f.getCumPct(bChar),tolerance);
+ assertEquals("a string pct",0.0,f.getPct(aString),tolerance);
+ assertEquals("a string cum pct",0.0,f.getCumPct(aString),tolerance);
+
+ f = new Frequency();
+ f.addValue("One");
+ try {
+ f.addValue(new Integer("One"));
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ // Check what happens when non-Comparable objects are added
+ @SuppressWarnings("deprecation")
+ public void testAddNonComparable(){
+ try {
+ f.addValue(new Object()); // This was previously OK
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ f.clear();
+ f.addValue(1);
+ try {
+ f.addValue(new Object());
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /** test empty table */
+ public void testEmptyTable() {
+ assertEquals("freq sum, empty table", 0, f.getSumFreq());
+ assertEquals("count, empty table", 0, f.getCount(0));
+ assertEquals("count, empty table",0, f.getCount(Integer.valueOf(0)));
+ assertEquals("cum freq, empty table", 0, f.getCumFreq(0));
+ assertEquals("cum freq, empty table", 0, f.getCumFreq("x"));
+ assertTrue("pct, empty table", Double.isNaN(f.getPct(0)));
+ assertTrue("pct, empty table", Double.isNaN(f.getPct(Integer.valueOf(0))));
+ assertTrue("cum pct, empty table", Double.isNaN(f.getCumPct(0)));
+ assertTrue("cum pct, empty table", Double.isNaN(f.getCumPct(Integer.valueOf(0))));
+ }
+
+ /**
+ * Tests toString()
+ */
+ public void testToString(){
+ f.addValue(oneL);
+ f.addValue(twoL);
+ f.addValue(oneI);
+ f.addValue(twoI);
+
+ String s = f.toString();
+ //System.out.println(s);
+ assertNotNull(s);
+ BufferedReader reader = new BufferedReader(new StringReader(s));
+ try {
+ String line = reader.readLine(); // header line
+ assertNotNull(line);
+
+ line = reader.readLine(); // one's or two's line
+ assertNotNull(line);
+
+ line = reader.readLine(); // one's or two's line
+ assertNotNull(line);
+
+ line = reader.readLine(); // no more elements
+ assertNull(line);
+ } catch(IOException ex){
+ fail(ex.getMessage());
+ }
+ }
+ @SuppressWarnings("deprecation")
+ public void testIntegerValues() {
+ Comparable<?> obj1 = null;
+ obj1 = Integer.valueOf(1);
+ Integer int1 = Integer.valueOf(1);
+ f.addValue(obj1);
+ f.addValue(int1);
+ f.addValue(2);
+ f.addValue(Long.valueOf(2));
+ assertEquals("Integer 1 count", 2, f.getCount(1));
+ assertEquals("Integer 1 count", 2, f.getCount(Integer.valueOf(1)));
+ assertEquals("Integer 1 count", 2, f.getCount(Long.valueOf(1)));
+ assertEquals("Integer 1 cumPct", 0.5, f.getCumPct(1), tolerance);
+ assertEquals("Integer 1 cumPct", 0.5, f.getCumPct(Long.valueOf(1)), tolerance);
+ assertEquals("Integer 1 cumPct", 0.5, f.getCumPct(Integer.valueOf(1)), tolerance);
+ Iterator<?> it = f.valuesIterator();
+ while (it.hasNext()) {
+ assertTrue(it.next() instanceof Long);
+ }
+ }
+
+ public void testSerial() {
+ f.addValue(oneL);
+ f.addValue(twoL);
+ f.addValue(oneI);
+ f.addValue(twoI);
+ assertEquals(f, TestUtils.serializeAndRecover(f));
+ }
+
+ public void testGetUniqueCount() {
+ assertEquals(0, f.getUniqueCount());
+ f.addValue(oneL);
+ assertEquals(1, f.getUniqueCount());
+ f.addValue(oneL);
+ assertEquals(1, f.getUniqueCount());
+ f.addValue(twoI);
+ assertEquals(2, f.getUniqueCount());
+ }
+}
+
diff --git a/src/test/java/org/apache/commons/math/stat/StatUtilsTest.java b/src/test/java/org/apache/commons/math/stat/StatUtilsTest.java
new file mode 100644
index 0000000..b41a3aa
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/StatUtilsTest.java
@@ -0,0 +1,468 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link StatUtils} class.
+ * @version $Revision: 1060124 $ $Date: 2011-01-18 00:00:52 +0100 (mar. 18 janv. 2011) $
+ */
+
+public final class StatUtilsTest extends TestCase {
+
+ private double one = 1;
+ private float two = 2;
+ private int three = 3;
+ private double mean = 2;
+ private double sumSq = 18;
+ private double sum = 8;
+ private double var = 0.666666666666666666667;
+ private double min = 1;
+ private double max = 3;
+ private double tolerance = 10E-15;
+ private double nan = Double.NaN;
+
+ public StatUtilsTest(String name) {
+ super(name);
+ }
+
+ /** test stats */
+ public void testStats() {
+ double[] values = new double[] { one, two, two, three };
+ assertEquals("sum", sum, StatUtils.sum(values), tolerance);
+ assertEquals("sumsq", sumSq, StatUtils.sumSq(values), tolerance);
+ assertEquals("var", var, StatUtils.variance(values), tolerance);
+ assertEquals("var with mean", var, StatUtils.variance(values, mean), tolerance);
+ assertEquals("mean", mean, StatUtils.mean(values), tolerance);
+ assertEquals("min", min, StatUtils.min(values), tolerance);
+ assertEquals("max", max, StatUtils.max(values), tolerance);
+ }
+
+ public void testN0andN1Conditions() throws Exception {
+ double[] values = new double[0];
+
+ assertTrue(
+ "Mean of n = 0 set should be NaN",
+ Double.isNaN(StatUtils.mean(values)));
+ assertTrue(
+ "Variance of n = 0 set should be NaN",
+ Double.isNaN(StatUtils.variance(values)));
+
+ values = new double[] { one };
+
+ assertTrue(
+ "Mean of n = 1 set should be value of single item n1",
+ StatUtils.mean(values) == one);
+ assertTrue(
+ "Variance of n = 1 set should be zero",
+ StatUtils.variance(values) == 0);
+ }
+
+ public void testArrayIndexConditions() throws Exception {
+ double[] values = { 1.0, 2.0, 3.0, 4.0 };
+
+ assertEquals(
+ "Sum not expected",
+ 5.0,
+ StatUtils.sum(values, 1, 2),
+ Double.MIN_VALUE);
+ assertEquals(
+ "Sum not expected",
+ 3.0,
+ StatUtils.sum(values, 0, 2),
+ Double.MIN_VALUE);
+ assertEquals(
+ "Sum not expected",
+ 7.0,
+ StatUtils.sum(values, 2, 2),
+ Double.MIN_VALUE);
+
+ try {
+ StatUtils.sum(values, 2, 3);
+ fail("Expected RuntimeException");
+ } catch (RuntimeException e) {
+ // expected
+ }
+
+ try {
+ StatUtils.sum(values, -1, 2);
+ fail("Expected RuntimeException");
+ } catch (RuntimeException e) {
+ // expected
+ }
+
+ }
+
+ public void testSumSq() {
+ double[] x = null;
+
+ // test null
+ try {
+ StatUtils.sumSq(x);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ StatUtils.sumSq(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.sumSq(x), tolerance);
+ TestUtils.assertEquals(Double.NaN, StatUtils.sumSq(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(4, StatUtils.sumSq(x), tolerance);
+ TestUtils.assertEquals(4, StatUtils.sumSq(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(18, StatUtils.sumSq(x), tolerance);
+ TestUtils.assertEquals(8, StatUtils.sumSq(x, 1, 2), tolerance);
+ }
+
+ public void testProduct() {
+ double[] x = null;
+
+ // test null
+ try {
+ StatUtils.product(x);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ StatUtils.product(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.product(x), tolerance);
+ TestUtils.assertEquals(Double.NaN, StatUtils.product(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(two, StatUtils.product(x), tolerance);
+ TestUtils.assertEquals(two, StatUtils.product(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(12, StatUtils.product(x), tolerance);
+ TestUtils.assertEquals(4, StatUtils.product(x, 1, 2), tolerance);
+ }
+
+ public void testSumLog() {
+ double[] x = null;
+
+ // test null
+ try {
+ StatUtils.sumLog(x);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ StatUtils.sumLog(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.sumLog(x), tolerance);
+ TestUtils.assertEquals(Double.NaN, StatUtils.sumLog(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(FastMath.log(two), StatUtils.sumLog(x), tolerance);
+ TestUtils.assertEquals(FastMath.log(two), StatUtils.sumLog(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(FastMath.log(one) + 2.0 * FastMath.log(two) + FastMath.log(three), StatUtils.sumLog(x), tolerance);
+ TestUtils.assertEquals(2.0 * FastMath.log(two), StatUtils.sumLog(x, 1, 2), tolerance);
+ }
+
+ public void testMean() {
+ double[] x = null;
+
+ try {
+ StatUtils.mean(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.mean(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(two, StatUtils.mean(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(2.5, StatUtils.mean(x, 2, 2), tolerance);
+ }
+
+ public void testVariance() {
+ double[] x = null;
+
+ try {
+ StatUtils.variance(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.variance(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(0.0, StatUtils.variance(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(0.5, StatUtils.variance(x, 2, 2), tolerance);
+
+ // test precomputed mean
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(0.5, StatUtils.variance(x,2.5, 2, 2), tolerance);
+ }
+
+ public void testMax() {
+ double[] x = null;
+
+ try {
+ StatUtils.max(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.max(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(two, StatUtils.max(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(three, StatUtils.max(x, 1, 3), tolerance);
+
+ // test first nan is ignored
+ x = new double[] {nan, two, three};
+ TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
+
+ // test middle nan is ignored
+ x = new double[] {one, nan, three};
+ TestUtils.assertEquals(three, StatUtils.max(x), tolerance);
+
+ // test last nan is ignored
+ x = new double[] {one, two, nan};
+ TestUtils.assertEquals(two, StatUtils.max(x), tolerance);
+
+ // test all nan returns nan
+ x = new double[] {nan, nan, nan};
+ TestUtils.assertEquals(nan, StatUtils.max(x), tolerance);
+ }
+
+ public void testMin() {
+ double[] x = null;
+
+ try {
+ StatUtils.min(x, 0, 4);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.min(x, 0, 0), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(two, StatUtils.min(x, 0, 1), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(two, StatUtils.min(x, 1, 3), tolerance);
+
+ // test first nan is ignored
+ x = new double[] {nan, two, three};
+ TestUtils.assertEquals(two, StatUtils.min(x), tolerance);
+
+ // test middle nan is ignored
+ x = new double[] {one, nan, three};
+ TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
+
+ // test last nan is ignored
+ x = new double[] {one, two, nan};
+ TestUtils.assertEquals(one, StatUtils.min(x), tolerance);
+
+ // test all nan returns nan
+ x = new double[] {nan, nan, nan};
+ TestUtils.assertEquals(nan, StatUtils.min(x), tolerance);
+ }
+
+ public void testPercentile() {
+ double[] x = null;
+
+ // test null
+ try {
+ StatUtils.percentile(x, .25);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ try {
+ StatUtils.percentile(x, 0, 4, 0.25);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // test empty
+ x = new double[] {};
+ TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 25), tolerance);
+ TestUtils.assertEquals(Double.NaN, StatUtils.percentile(x, 0, 0, 25), tolerance);
+
+ // test one
+ x = new double[] {two};
+ TestUtils.assertEquals(two, StatUtils.percentile(x, 25), tolerance);
+ TestUtils.assertEquals(two, StatUtils.percentile(x, 0, 1, 25), tolerance);
+
+ // test many
+ x = new double[] {one, two, two, three};
+ TestUtils.assertEquals(2.5, StatUtils.percentile(x, 70), tolerance);
+ TestUtils.assertEquals(2.5, StatUtils.percentile(x, 1, 3, 62.5), tolerance);
+ }
+
+ public void testDifferenceStats() throws Exception {
+ double sample1[] = {1d, 2d, 3d, 4d};
+ double sample2[] = {1d, 3d, 4d, 2d};
+ double diff[] = {0d, -1d, -1d, 2d};
+ double small[] = {1d, 4d};
+ double meanDifference = StatUtils.meanDifference(sample1, sample2);
+ assertEquals(StatUtils.sumDifference(sample1, sample2), StatUtils.sum(diff), tolerance);
+ assertEquals(meanDifference, StatUtils.mean(diff), tolerance);
+ assertEquals(StatUtils.varianceDifference(sample1, sample2, meanDifference),
+ StatUtils.variance(diff), tolerance);
+ try {
+ StatUtils.meanDifference(sample1, small);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ StatUtils.varianceDifference(sample1, small, meanDifference);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ double[] single = {1.0};
+ StatUtils.varianceDifference(single, single, meanDifference);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testGeometricMean() throws Exception {
+ double[] test = null;
+ try {
+ StatUtils.geometricMean(test);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ test = new double[] {2, 4, 6, 8};
+ assertEquals(FastMath.exp(0.25d * StatUtils.sumLog(test)),
+ StatUtils.geometricMean(test), Double.MIN_VALUE);
+ assertEquals(FastMath.exp(0.5 * StatUtils.sumLog(test, 0, 2)),
+ StatUtils.geometricMean(test, 0, 2), Double.MIN_VALUE);
+ }
+
+
+ /**
+ * Run the test with the values 50 and 100 and assume standardized values
+ */
+
+ public void testNormalize1() {
+ double sample[] = { 50, 100 };
+ double expectedSample[] = { -25 / Math.sqrt(1250), 25 / Math.sqrt(1250) };
+ double[] out = StatUtils.normalize(sample);
+ for (int i = 0; i < out.length; i++) {
+ assertEquals(out[i], expectedSample[i]);
+ }
+
+ }
+
+ /**
+ * Run with 77 random values, assuming that the outcome has a mean of 0 and a standard deviation of 1 with a
+ * precision of 1E-10.
+ */
+
+ public void testNormalize2() {
+ // create an sample with 77 values
+ int length = 77;
+ double sample[] = new double[length];
+ for (int i = 0; i < length; i++) {
+ sample[i] = Math.random();
+ }
+ // normalize this sample
+ double standardizedSample[] = StatUtils.normalize(sample);
+
+ DescriptiveStatistics stats = new DescriptiveStatistics();
+ // Add the data from the array
+ for (int i = 0; i < length; i++) {
+ stats.addValue(standardizedSample[i]);
+ }
+ // the calculations do have a limited precision
+ double distance = 1E-10;
+ // check the mean an standard deviation
+ assertEquals(0.0, stats.getMean(), distance);
+ assertEquals(1.0, stats.getStandardDeviation(), distance);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPointTest.java b/src/test/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPointTest.java
new file mode 100644
index 0000000..762bb55
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/clustering/EuclideanIntegerPointTest.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.clustering;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+import org.junit.Test;
+
+public class EuclideanIntegerPointTest {
+
+ @Test
+ public void testArrayIsReference() {
+ int[] array = { -3, -2, -1, 0, 1 };
+ assertTrue(array == new EuclideanIntegerPoint(array).getPoint());
+ }
+
+ @Test
+ public void testDistance() {
+ EuclideanIntegerPoint e1 = new EuclideanIntegerPoint(new int[] { -3, -2, -1, 0, 1 });
+ EuclideanIntegerPoint e2 = new EuclideanIntegerPoint(new int[] { 1, 0, -1, 1, 1 });
+ assertEquals(FastMath.sqrt(21.0), e1.distanceFrom(e2), 1.0e-15);
+ assertEquals(0.0, e1.distanceFrom(e1), 1.0e-15);
+ assertEquals(0.0, e2.distanceFrom(e2), 1.0e-15);
+ }
+
+ @Test
+ public void testCentroid() {
+ List<EuclideanIntegerPoint> list = new ArrayList<EuclideanIntegerPoint>();
+ list.add(new EuclideanIntegerPoint(new int[] { 1, 3 }));
+ list.add(new EuclideanIntegerPoint(new int[] { 2, 2 }));
+ list.add(new EuclideanIntegerPoint(new int[] { 3, 3 }));
+ list.add(new EuclideanIntegerPoint(new int[] { 2, 4 }));
+ EuclideanIntegerPoint c = list.get(0).centroidOf(list);
+ assertEquals(2, c.getPoint()[0]);
+ assertEquals(3, c.getPoint()[1]);
+ }
+
+ @Test
+ public void testSerial() {
+ EuclideanIntegerPoint p = new EuclideanIntegerPoint(new int[] { -3, -2, -1, 0, 1 });
+ assertEquals(p, TestUtils.serializeAndRecover(p));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClustererTest.java b/src/test/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClustererTest.java
new file mode 100644
index 0000000..33b2d8a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/clustering/KMeansPlusPlusClustererTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.clustering;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class KMeansPlusPlusClustererTest {
+
+ @Test
+ public void dimension2() {
+ KMeansPlusPlusClusterer<EuclideanIntegerPoint> transformer =
+ new KMeansPlusPlusClusterer<EuclideanIntegerPoint>(new Random(1746432956321l));
+ EuclideanIntegerPoint[] points = new EuclideanIntegerPoint[] {
+
+ // first expected cluster
+ new EuclideanIntegerPoint(new int[] { -15, 3 }),
+ new EuclideanIntegerPoint(new int[] { -15, 4 }),
+ new EuclideanIntegerPoint(new int[] { -15, 5 }),
+ new EuclideanIntegerPoint(new int[] { -14, 3 }),
+ new EuclideanIntegerPoint(new int[] { -14, 5 }),
+ new EuclideanIntegerPoint(new int[] { -13, 3 }),
+ new EuclideanIntegerPoint(new int[] { -13, 4 }),
+ new EuclideanIntegerPoint(new int[] { -13, 5 }),
+
+ // second expected cluster
+ new EuclideanIntegerPoint(new int[] { -1, 0 }),
+ new EuclideanIntegerPoint(new int[] { -1, -1 }),
+ new EuclideanIntegerPoint(new int[] { 0, -1 }),
+ new EuclideanIntegerPoint(new int[] { 1, -1 }),
+ new EuclideanIntegerPoint(new int[] { 1, -2 }),
+
+ // third expected cluster
+ new EuclideanIntegerPoint(new int[] { 13, 3 }),
+ new EuclideanIntegerPoint(new int[] { 13, 4 }),
+ new EuclideanIntegerPoint(new int[] { 14, 4 }),
+ new EuclideanIntegerPoint(new int[] { 14, 7 }),
+ new EuclideanIntegerPoint(new int[] { 16, 5 }),
+ new EuclideanIntegerPoint(new int[] { 16, 6 }),
+ new EuclideanIntegerPoint(new int[] { 17, 4 }),
+ new EuclideanIntegerPoint(new int[] { 17, 7 })
+
+ };
+ List<Cluster<EuclideanIntegerPoint>> clusters =
+ transformer.cluster(Arrays.asList(points), 3, 10);
+
+ assertEquals(3, clusters.size());
+ boolean cluster1Found = false;
+ boolean cluster2Found = false;
+ boolean cluster3Found = false;
+ for (Cluster<EuclideanIntegerPoint> cluster : clusters) {
+ int[] center = cluster.getCenter().getPoint();
+ if (center[0] < 0) {
+ cluster1Found = true;
+ assertEquals(8, cluster.getPoints().size());
+ assertEquals(-14, center[0]);
+ assertEquals( 4, center[1]);
+ } else if (center[1] < 0) {
+ cluster2Found = true;
+ assertEquals(5, cluster.getPoints().size());
+ assertEquals( 0, center[0]);
+ assertEquals(-1, center[1]);
+ } else {
+ cluster3Found = true;
+ assertEquals(8, cluster.getPoints().size());
+ assertEquals(15, center[0]);
+ assertEquals(5, center[1]);
+ }
+ }
+ assertTrue(cluster1Found);
+ assertTrue(cluster2Found);
+ assertTrue(cluster3Found);
+
+ }
+
+ /**
+ * JIRA: MATH-305
+ *
+ * Two points, one cluster, one iteration
+ */
+ @Test
+ public void testPerformClusterAnalysisDegenerate() {
+ KMeansPlusPlusClusterer<EuclideanIntegerPoint> transformer = new KMeansPlusPlusClusterer<EuclideanIntegerPoint>(
+ new Random(1746432956321l));
+ EuclideanIntegerPoint[] points = new EuclideanIntegerPoint[] {
+ new EuclideanIntegerPoint(new int[] { 1959, 325100 }),
+ new EuclideanIntegerPoint(new int[] { 1960, 373200 }), };
+ List<Cluster<EuclideanIntegerPoint>> clusters = transformer.cluster(Arrays.asList(points), 1, 1);
+ assertEquals(1, clusters.size());
+ assertEquals(2, (clusters.get(0).getPoints().size()));
+ EuclideanIntegerPoint pt1 = new EuclideanIntegerPoint(new int[] { 1959, 325100 });
+ EuclideanIntegerPoint pt2 = new EuclideanIntegerPoint(new int[] { 1960, 373200 });
+ assertTrue(clusters.get(0).getPoints().contains(pt1));
+ assertTrue(clusters.get(0).getPoints().contains(pt2));
+
+ }
+
+ @Test
+ public void testCertainSpace() {
+ KMeansPlusPlusClusterer.EmptyClusterStrategy[] strategies = {
+ KMeansPlusPlusClusterer.EmptyClusterStrategy.LARGEST_VARIANCE,
+ KMeansPlusPlusClusterer.EmptyClusterStrategy.LARGEST_POINTS_NUMBER,
+ KMeansPlusPlusClusterer.EmptyClusterStrategy.FARTHEST_POINT
+ };
+ for (KMeansPlusPlusClusterer.EmptyClusterStrategy strategy : strategies) {
+ KMeansPlusPlusClusterer<EuclideanIntegerPoint> transformer =
+ new KMeansPlusPlusClusterer<EuclideanIntegerPoint>(new Random(1746432956321l), strategy);
+ int numberOfVariables = 27;
+ // initialise testvalues
+ int position1 = 1;
+ int position2 = position1 + numberOfVariables;
+ int position3 = position2 + numberOfVariables;
+ int position4 = position3 + numberOfVariables;
+ // testvalues will be multiplied
+ int multiplier = 1000000;
+
+ EuclideanIntegerPoint[] breakingPoints = new EuclideanIntegerPoint[numberOfVariables];
+ // define the space which will break the cluster algorithm
+ for (int i = 0; i < numberOfVariables; i++) {
+ int points[] = { position1, position2, position3, position4 };
+ // multiply the values
+ for (int j = 0; j < points.length; j++) {
+ points[j] = points[j] * multiplier;
+ }
+ EuclideanIntegerPoint euclideanIntegerPoint = new EuclideanIntegerPoint(points);
+ breakingPoints[i] = euclideanIntegerPoint;
+ position1 = position1 + numberOfVariables;
+ position2 = position2 + numberOfVariables;
+ position3 = position3 + numberOfVariables;
+ position4 = position4 + numberOfVariables;
+ }
+
+ for (int n = 2; n < 27; ++n) {
+ List<Cluster<EuclideanIntegerPoint>> clusters =
+ transformer.cluster(Arrays.asList(breakingPoints), n, 100);
+ Assert.assertEquals(n, clusters.size());
+ int sum = 0;
+ for (Cluster<EuclideanIntegerPoint> cluster : clusters) {
+ sum += cluster.getPoints().size();
+ }
+ Assert.assertEquals(numberOfVariables, sum);
+ }
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/correlation/CovarianceTest.java b/src/test/java/org/apache/commons/math/stat/correlation/CovarianceTest.java
new file mode 100644
index 0000000..b603bb9
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/correlation/CovarianceTest.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.stat.correlation;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.Array2DRowRealMatrix;
+import org.apache.commons.math.stat.descriptive.moment.Variance;
+
+import junit.framework.TestCase;
+
+public class CovarianceTest extends TestCase {
+
+ protected final double[] longleyData = new double[] {
+ 60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962
+ };
+
+ protected final double[] swissData = new double[] {
+ 80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33
+ };
+
+
+ /**
+ * Test Longley dataset against R.
+ * Data Source: J. Longley (1967) "An Appraisal of Least Squares
+ * Programs for the Electronic Computer from the Point of View of the User"
+ * Journal of the American Statistical Association, vol. 62. September,
+ * pp. 819-841.
+ *
+ * Data are from NIST:
+ * http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Longley.dat
+ */
+ public void testLongly() {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ RealMatrix covarianceMatrix = new Covariance(matrix).getCovarianceMatrix();
+ double[] rData = new double[] {
+ 12333921.73333333246, 3.679666000000000e+04, 343330206.333333313,
+ 1649102.666666666744, 1117681.066666666651, 23461965.733333334, 16240.93333333333248,
+ 36796.66000000000, 1.164576250000000e+02, 1063604.115416667,
+ 6258.666250000000, 3490.253750000000, 73503.000000000, 50.92333333333334,
+ 343330206.33333331347, 1.063604115416667e+06, 9879353659.329166412,
+ 56124369.854166664183, 30880428.345833335072, 685240944.600000024, 470977.90000000002328,
+ 1649102.66666666674, 6.258666250000000e+03, 56124369.854166664,
+ 873223.429166666698, -115378.762499999997, 4462741.533333333, 2973.03333333333330,
+ 1117681.06666666665, 3.490253750000000e+03, 30880428.345833335,
+ -115378.762499999997, 484304.095833333326, 1764098.133333333, 1382.43333333333339,
+ 23461965.73333333433, 7.350300000000000e+04, 685240944.600000024,
+ 4462741.533333333209, 1764098.133333333302, 48387348.933333330, 32917.40000000000146,
+ 16240.93333333333, 5.092333333333334e+01, 470977.900000000,
+ 2973.033333333333, 1382.433333333333, 32917.40000000, 22.66666666666667
+ };
+
+ TestUtils.assertEquals("covariance matrix", createRealMatrix(rData, 7, 7), covarianceMatrix, 10E-9);
+
+ }
+
+ /**
+ * Test R Swiss fertility dataset against R.
+ * Data Source: R datasets package
+ */
+ public void testSwissFertility() {
+ RealMatrix matrix = createRealMatrix(swissData, 47, 5);
+ RealMatrix covarianceMatrix = new Covariance(matrix).getCovarianceMatrix();
+ double[] rData = new double[] {
+ 156.0424976873265, 100.1691489361702, -64.36692876965772, -79.7295097132285, 241.5632030527289,
+ 100.169148936170251, 515.7994172062905, -124.39283071230344, -139.6574005550416, 379.9043755781684,
+ -64.3669287696577, -124.3928307123034, 63.64662349676226, 53.5758556891767, -190.5606105457909,
+ -79.7295097132285, -139.6574005550416, 53.57585568917669, 92.4560592044403, -61.6988297872340,
+ 241.5632030527289, 379.9043755781684, -190.56061054579092, -61.6988297872340, 1739.2945371877890
+ };
+
+ TestUtils.assertEquals("covariance matrix", createRealMatrix(rData, 5, 5), covarianceMatrix, 10E-13);
+ }
+
+ /**
+ * Constant column
+ */
+ public void testConstant() {
+ double[] noVariance = new double[] {1, 1, 1, 1};
+ double[] values = new double[] {1, 2, 3, 4};
+ assertEquals(0d, new Covariance().covariance(noVariance, values, true), Double.MIN_VALUE);
+ assertEquals(0d, new Covariance().covariance(noVariance, noVariance, true), Double.MIN_VALUE);
+ }
+
+
+ /**
+ * Insufficient data
+ */
+ public void testInsufficientData() {
+ double[] one = new double[] {1};
+ double[] two = new double[] {2};
+ try {
+ new Covariance().covariance(one, two, false);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ RealMatrix matrix = new Array2DRowRealMatrix(new double[][] {{0},{1}});
+ try {
+ new Covariance(matrix);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ /**
+ * Verify that diagonal entries are consistent with Variance computation and matrix matches
+ * column-by-column covariances
+ */
+ public void testConsistency() {
+ final RealMatrix matrix = createRealMatrix(swissData, 47, 5);
+ final RealMatrix covarianceMatrix = new Covariance(matrix).getCovarianceMatrix();
+
+ // Variances on the diagonal
+ Variance variance = new Variance();
+ for (int i = 0; i < 5; i++) {
+ assertEquals(variance.evaluate(matrix.getColumn(i)), covarianceMatrix.getEntry(i,i), 10E-14);
+ }
+
+ // Symmetry, column-consistency
+ assertEquals(covarianceMatrix.getEntry(2, 3),
+ new Covariance().covariance(matrix.getColumn(2), matrix.getColumn(3), true), 10E-14);
+ assertEquals(covarianceMatrix.getEntry(2, 3), covarianceMatrix.getEntry(3, 2), Double.MIN_VALUE);
+
+ // All columns same -> all entries = column variance
+ RealMatrix repeatedColumns = new Array2DRowRealMatrix(47, 3);
+ for (int i = 0; i < 3; i++) {
+ repeatedColumns.setColumnMatrix(i, matrix.getColumnMatrix(0));
+ }
+ RealMatrix repeatedCovarianceMatrix = new Covariance(repeatedColumns).getCovarianceMatrix();
+ double columnVariance = variance.evaluate(matrix.getColumn(0));
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ assertEquals(columnVariance, repeatedCovarianceMatrix.getEntry(i, j), 10E-14);
+ }
+ }
+
+ // Check bias-correction defaults
+ double[][] data = matrix.getData();
+ TestUtils.assertEquals("Covariances",
+ covarianceMatrix, new Covariance().computeCovarianceMatrix(data),Double.MIN_VALUE);
+ TestUtils.assertEquals("Covariances",
+ covarianceMatrix, new Covariance().computeCovarianceMatrix(data, true),Double.MIN_VALUE);
+
+ double[] x = data[0];
+ double[] y = data[1];
+ assertEquals(new Covariance().covariance(x, y),
+ new Covariance().covariance(x, y, true), Double.MIN_VALUE);
+ }
+
+ protected RealMatrix createRealMatrix(double[] data, int nRows, int nCols) {
+ double[][] matrixData = new double[nRows][nCols];
+ int ptr = 0;
+ for (int i = 0; i < nRows; i++) {
+ System.arraycopy(data, ptr, matrixData[i], 0, nCols);
+ ptr += nCols;
+ }
+ return new Array2DRowRealMatrix(matrixData);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/correlation/PearsonsCorrelationTest.java b/src/test/java/org/apache/commons/math/stat/correlation/PearsonsCorrelationTest.java
new file mode 100644
index 0000000..bf34ef2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/correlation/PearsonsCorrelationTest.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.correlation;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.distribution.TDistribution;
+import org.apache.commons.math.distribution.TDistributionImpl;
+import org.apache.commons.math.linear.RealMatrix;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.util.FastMath;
+
+import junit.framework.TestCase;
+
+public class PearsonsCorrelationTest extends TestCase {
+
+ protected final double[] longleyData = new double[] {
+ 60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962
+ };
+
+ protected final double[] swissData = new double[] {
+ 80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33
+ };
+
+
+ /**
+ * Test Longley dataset against R.
+ */
+ public void testLongly() throws Exception {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(matrix);
+ RealMatrix correlationMatrix = corrInstance.getCorrelationMatrix();
+ double[] rData = new double[] {
+ 1.000000000000000, 0.9708985250610560, 0.9835516111796693, 0.5024980838759942,
+ 0.4573073999764817, 0.960390571594376, 0.9713294591921188,
+ 0.970898525061056, 1.0000000000000000, 0.9915891780247822, 0.6206333925590966,
+ 0.4647441876006747, 0.979163432977498, 0.9911491900672053,
+ 0.983551611179669, 0.9915891780247822, 1.0000000000000000, 0.6042609398895580,
+ 0.4464367918926265, 0.991090069458478, 0.9952734837647849,
+ 0.502498083875994, 0.6206333925590966, 0.6042609398895580, 1.0000000000000000,
+ -0.1774206295018783, 0.686551516365312, 0.6682566045621746,
+ 0.457307399976482, 0.4647441876006747, 0.4464367918926265, -0.1774206295018783,
+ 1.0000000000000000, 0.364416267189032, 0.4172451498349454,
+ 0.960390571594376, 0.9791634329774981, 0.9910900694584777, 0.6865515163653120,
+ 0.3644162671890320, 1.000000000000000, 0.9939528462329257,
+ 0.971329459192119, 0.9911491900672053, 0.9952734837647849, 0.6682566045621746,
+ 0.4172451498349454, 0.993952846232926, 1.0000000000000000
+ };
+ TestUtils.assertEquals("correlation matrix", createRealMatrix(rData, 7, 7), correlationMatrix, 10E-15);
+
+ double[] rPvalues = new double[] {
+ 4.38904690369668e-10,
+ 8.36353208910623e-12, 7.8159700933611e-14,
+ 0.0472894097790304, 0.01030636128354301, 0.01316878049026582,
+ 0.0749178049642416, 0.06971758330341182, 0.0830166169296545, 0.510948586323452,
+ 3.693245043123738e-09, 4.327782576751815e-11, 1.167954621905665e-13, 0.00331028281967516, 0.1652293725106684,
+ 3.95834476307755e-10, 1.114663916723657e-13, 1.332267629550188e-15, 0.00466039138541463, 0.1078477071581498, 7.771561172376096e-15
+ };
+ RealMatrix rPMatrix = createLowerTriangularRealMatrix(rPvalues, 7);
+ fillUpper(rPMatrix, 0d);
+ TestUtils.assertEquals("correlation p values", rPMatrix, corrInstance.getCorrelationPValues(), 10E-15);
+ }
+
+ /**
+ * Test R Swiss fertility dataset against R.
+ */
+ public void testSwissFertility() throws Exception {
+ RealMatrix matrix = createRealMatrix(swissData, 47, 5);
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(matrix);
+ RealMatrix correlationMatrix = corrInstance.getCorrelationMatrix();
+ double[] rData = new double[] {
+ 1.0000000000000000, 0.3530791836199747, -0.6458827064572875, -0.6637888570350691, 0.4636847006517939,
+ 0.3530791836199747, 1.0000000000000000,-0.6865422086171366, -0.6395225189483201, 0.4010950530487398,
+ -0.6458827064572875, -0.6865422086171366, 1.0000000000000000, 0.6984152962884830, -0.5727418060641666,
+ -0.6637888570350691, -0.6395225189483201, 0.6984152962884830, 1.0000000000000000, -0.1538589170909148,
+ 0.4636847006517939, 0.4010950530487398, -0.5727418060641666, -0.1538589170909148, 1.0000000000000000
+ };
+ TestUtils.assertEquals("correlation matrix", createRealMatrix(rData, 5, 5), correlationMatrix, 10E-15);
+
+ double[] rPvalues = new double[] {
+ 0.01491720061472623,
+ 9.45043734069043e-07, 9.95151527133974e-08,
+ 3.658616965962355e-07, 1.304590105694471e-06, 4.811397236181847e-08,
+ 0.001028523190118147, 0.005204433539191644, 2.588307925380906e-05, 0.301807756132683
+ };
+ RealMatrix rPMatrix = createLowerTriangularRealMatrix(rPvalues, 5);
+ fillUpper(rPMatrix, 0d);
+ TestUtils.assertEquals("correlation p values", rPMatrix, corrInstance.getCorrelationPValues(), 10E-15);
+ }
+
+ /**
+ * Test p-value near 0. JIRA: MATH-371
+ */
+ public void testPValueNearZero() throws Exception {
+ /*
+ * Create a dataset that has r -> 1, p -> 0 as dimension increases.
+ * Prior to the fix for MATH-371, p vanished for dimension >= 14.
+ * Post fix, p-values diminish smoothly, vanishing at dimension = 127.
+ * Tested value is ~1E-303.
+ */
+ int dimension = 120;
+ double[][] data = new double[dimension][2];
+ for (int i = 0; i < dimension; i++) {
+ data[i][0] = i;
+ data[i][1] = i + 1/((double)i + 1);
+ }
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(data);
+ assertTrue(corrInstance.getCorrelationPValues().getEntry(0, 1) > 0);
+ }
+
+
+ /**
+ * Constant column
+ */
+ public void testConstant() {
+ double[] noVariance = new double[] {1, 1, 1, 1};
+ double[] values = new double[] {1, 2, 3, 4};
+ assertTrue(Double.isNaN(new PearsonsCorrelation().correlation(noVariance, values)));
+ }
+
+
+ /**
+ * Insufficient data
+ */
+
+ public void testInsufficientData() {
+ double[] one = new double[] {1};
+ double[] two = new double[] {2};
+ try {
+ new PearsonsCorrelation().correlation(one, two);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ RealMatrix matrix = new BlockRealMatrix(new double[][] {{0},{1}});
+ try {
+ new PearsonsCorrelation(matrix);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ /**
+ * Verify that direct t-tests using standard error estimates are consistent
+ * with reported p-values
+ */
+ public void testStdErrorConsistency() throws Exception {
+ TDistribution tDistribution = new TDistributionImpl(45);
+ RealMatrix matrix = createRealMatrix(swissData, 47, 5);
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(matrix);
+ RealMatrix rValues = corrInstance.getCorrelationMatrix();
+ RealMatrix pValues = corrInstance.getCorrelationPValues();
+ RealMatrix stdErrors = corrInstance.getCorrelationStandardErrors();
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < i; j++) {
+ double t = FastMath.abs(rValues.getEntry(i, j)) / stdErrors.getEntry(i, j);
+ double p = 2 * (1 - tDistribution.cumulativeProbability(t));
+ assertEquals(p, pValues.getEntry(i, j), 10E-15);
+ }
+ }
+ }
+
+ /**
+ * Verify that creating correlation from covariance gives same results as
+ * direct computation from the original matrix
+ */
+ public void testCovarianceConsistency() throws Exception {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(matrix);
+ Covariance covInstance = new Covariance(matrix);
+ PearsonsCorrelation corrFromCovInstance = new PearsonsCorrelation(covInstance);
+ TestUtils.assertEquals("correlation values", corrInstance.getCorrelationMatrix(),
+ corrFromCovInstance.getCorrelationMatrix(), 10E-15);
+ TestUtils.assertEquals("p values", corrInstance.getCorrelationPValues(),
+ corrFromCovInstance.getCorrelationPValues(), 10E-15);
+ TestUtils.assertEquals("standard errors", corrInstance.getCorrelationStandardErrors(),
+ corrFromCovInstance.getCorrelationStandardErrors(), 10E-15);
+
+ PearsonsCorrelation corrFromCovInstance2 =
+ new PearsonsCorrelation(covInstance.getCovarianceMatrix(), 16);
+ TestUtils.assertEquals("correlation values", corrInstance.getCorrelationMatrix(),
+ corrFromCovInstance2.getCorrelationMatrix(), 10E-15);
+ TestUtils.assertEquals("p values", corrInstance.getCorrelationPValues(),
+ corrFromCovInstance2.getCorrelationPValues(), 10E-15);
+ TestUtils.assertEquals("standard errors", corrInstance.getCorrelationStandardErrors(),
+ corrFromCovInstance2.getCorrelationStandardErrors(), 10E-15);
+ }
+
+
+ public void testConsistency() {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ PearsonsCorrelation corrInstance = new PearsonsCorrelation(matrix);
+ double[][] data = matrix.getData();
+ double[] x = matrix.getColumn(0);
+ double[] y = matrix.getColumn(1);
+ assertEquals(new PearsonsCorrelation().correlation(x, y),
+ corrInstance.getCorrelationMatrix().getEntry(0, 1), Double.MIN_VALUE);
+ TestUtils.assertEquals("Correlation matrix", corrInstance.getCorrelationMatrix(),
+ new PearsonsCorrelation().computeCorrelationMatrix(data), Double.MIN_VALUE);
+ }
+
+ protected RealMatrix createRealMatrix(double[] data, int nRows, int nCols) {
+ double[][] matrixData = new double[nRows][nCols];
+ int ptr = 0;
+ for (int i = 0; i < nRows; i++) {
+ System.arraycopy(data, ptr, matrixData[i], 0, nCols);
+ ptr += nCols;
+ }
+ return new BlockRealMatrix(matrixData);
+ }
+
+ protected RealMatrix createLowerTriangularRealMatrix(double[] data, int dimension) {
+ int ptr = 0;
+ RealMatrix result = new BlockRealMatrix(dimension, dimension);
+ for (int i = 1; i < dimension; i++) {
+ for (int j = 0; j < i; j++) {
+ result.setEntry(i, j, data[ptr]);
+ ptr++;
+ }
+ }
+ return result;
+ }
+
+ protected void fillUpper(RealMatrix matrix, double diagonalValue) {
+ int dimension = matrix.getColumnDimension();
+ for (int i = 0; i < dimension; i++) {
+ matrix.setEntry(i, i, diagonalValue);
+ for (int j = i+1; j < dimension; j++) {
+ matrix.setEntry(i, j, matrix.getEntry(j, i));
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/correlation/SpearmansRankCorrelationTest.java b/src/test/java/org/apache/commons/math/stat/correlation/SpearmansRankCorrelationTest.java
new file mode 100644
index 0000000..99eb065
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/correlation/SpearmansRankCorrelationTest.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.stat.correlation;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.BlockRealMatrix;
+import org.apache.commons.math.linear.RealMatrix;
+
+/**
+ * Test cases for Spearman's rank correlation
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class SpearmansRankCorrelationTest extends PearsonsCorrelationTest {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Test Longley dataset against R.
+ */
+ @Override
+ public void testLongly() throws Exception {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ SpearmansCorrelation corrInstance = new SpearmansCorrelation(matrix);
+ RealMatrix correlationMatrix = corrInstance.getCorrelationMatrix();
+ double[] rData = new double[] {
+ 1, 0.982352941176471, 0.985294117647059, 0.564705882352941, 0.2264705882352941, 0.976470588235294,
+ 0.976470588235294, 0.982352941176471, 1, 0.997058823529412, 0.664705882352941, 0.2205882352941176,
+ 0.997058823529412, 0.997058823529412, 0.985294117647059, 0.997058823529412, 1, 0.638235294117647,
+ 0.2235294117647059, 0.9941176470588236, 0.9941176470588236, 0.564705882352941, 0.664705882352941,
+ 0.638235294117647, 1, -0.3411764705882353, 0.685294117647059, 0.685294117647059, 0.2264705882352941,
+ 0.2205882352941176, 0.2235294117647059, -0.3411764705882353, 1, 0.2264705882352941, 0.2264705882352941,
+ 0.976470588235294, 0.997058823529412, 0.9941176470588236, 0.685294117647059, 0.2264705882352941, 1, 1,
+ 0.976470588235294, 0.997058823529412, 0.9941176470588236, 0.685294117647059, 0.2264705882352941, 1, 1
+ };
+ TestUtils.assertEquals("Spearman's correlation matrix", createRealMatrix(rData, 7, 7), correlationMatrix, 10E-15);
+ }
+
+ /**
+ * Test R swiss fertility dataset.
+ */
+ public void testSwiss() throws Exception {
+ RealMatrix matrix = createRealMatrix(swissData, 47, 5);
+ SpearmansCorrelation corrInstance = new SpearmansCorrelation(matrix);
+ RealMatrix correlationMatrix = corrInstance.getCorrelationMatrix();
+ double[] rData = new double[] {
+ 1, 0.2426642769364176, -0.660902996352354, -0.443257690360988, 0.4136455623012432,
+ 0.2426642769364176, 1, -0.598859938748963, -0.650463814145816, 0.2886878090882852,
+ -0.660902996352354, -0.598859938748963, 1, 0.674603831406147, -0.4750575257171745,
+ -0.443257690360988, -0.650463814145816, 0.674603831406147, 1, -0.1444163088302244,
+ 0.4136455623012432, 0.2886878090882852, -0.4750575257171745, -0.1444163088302244, 1
+ };
+ TestUtils.assertEquals("Spearman's correlation matrix", createRealMatrix(rData, 5, 5), correlationMatrix, 10E-15);
+ }
+
+ /**
+ * Constant column
+ */
+ @Override
+ public void testConstant() {
+ double[] noVariance = new double[] {1, 1, 1, 1};
+ double[] values = new double[] {1, 2, 3, 4};
+ assertTrue(Double.isNaN(new SpearmansCorrelation().correlation(noVariance, values)));
+ }
+
+ /**
+ * Insufficient data
+ */
+ @Override
+ public void testInsufficientData() {
+ double[] one = new double[] {1};
+ double[] two = new double[] {2};
+ try {
+ new SpearmansCorrelation().correlation(one, two);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ RealMatrix matrix = new BlockRealMatrix(new double[][] {{0},{1}});
+ try {
+ new SpearmansCorrelation(matrix);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ @Override
+ public void testConsistency() {
+ RealMatrix matrix = createRealMatrix(longleyData, 16, 7);
+ SpearmansCorrelation corrInstance = new SpearmansCorrelation(matrix);
+ double[][] data = matrix.getData();
+ double[] x = matrix.getColumn(0);
+ double[] y = matrix.getColumn(1);
+ assertEquals(new SpearmansCorrelation().correlation(x, y),
+ corrInstance.getCorrelationMatrix().getEntry(0, 1), Double.MIN_VALUE);
+ TestUtils.assertEquals("Correlation matrix", corrInstance.getCorrelationMatrix(),
+ new SpearmansCorrelation().computeCorrelationMatrix(data), Double.MIN_VALUE);
+ }
+
+ // Not relevant here
+ @Override
+ public void testStdErrorConsistency() throws Exception {}
+ @Override
+ public void testCovarianceConsistency() throws Exception {}
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/data/CertifiedDataAbstractTest.java b/src/test/java/org/apache/commons/math/stat/data/CertifiedDataAbstractTest.java
new file mode 100644
index 0000000..319758b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/data/CertifiedDataAbstractTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.stat.data;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public abstract class CertifiedDataAbstractTest extends TestCase {
+
+ private DescriptiveStatistics descriptives;
+
+ private SummaryStatistics summaries;
+
+ private Map<String, Double> certifiedValues;
+
+ @Override
+ protected void setUp() throws Exception {
+ descriptives = new DescriptiveStatistics();
+ summaries = new SummaryStatistics();
+ certifiedValues = new HashMap<String, Double>();
+
+ loadData();
+ }
+
+ private void loadData() throws IOException {
+ BufferedReader in = null;
+
+ try {
+ URL resourceURL = getClass().getClassLoader().getResource(getResourceName());
+ in = new BufferedReader(new InputStreamReader(resourceURL.openStream()));
+
+ String line = in.readLine();
+ while (line != null) {
+
+ /* this call to StringUtils did little for the
+ * following conditional structure
+ */
+ line = line.trim();
+
+ // not empty line or comment
+ if (!("".equals(line) || line.startsWith("#"))) {
+ int n = line.indexOf('=');
+ if (n == -1) {
+ // data value
+ double value = Double.parseDouble(line);
+ descriptives.addValue(value);
+ summaries.addValue(value);
+ } else {
+ // certified value
+ String name = line.substring(0, n).trim();
+ String valueString = line.substring(n + 1).trim();
+ Double value = Double.valueOf(valueString);
+ certifiedValues.put(name, value);
+ }
+ }
+ line = in.readLine();
+ }
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+ }
+
+ protected abstract String getResourceName();
+
+ protected double getMaximumAbsoluteError() {
+ return 1.0e-5;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ descriptives.clear();
+ descriptives = null;
+
+ summaries.clear();
+ summaries = null;
+
+ certifiedValues.clear();
+ certifiedValues = null;
+ }
+
+ public void testCertifiedValues() {
+ for (String name : certifiedValues.keySet()) {
+ Double expectedValue = certifiedValues.get(name);
+
+ Double summariesValue = getProperty(summaries, name);
+ if (summariesValue != null) {
+ TestUtils.assertEquals("summary value for " + name + " is incorrect.",
+ summariesValue.doubleValue(), expectedValue.doubleValue(),
+ getMaximumAbsoluteError());
+ }
+
+ Double descriptivesValue = getProperty(descriptives, name);
+ if (descriptivesValue != null) {
+ TestUtils.assertEquals("descriptive value for " + name + " is incorrect.",
+ descriptivesValue.doubleValue(), expectedValue.doubleValue(),
+ getMaximumAbsoluteError());
+ }
+ }
+ }
+
+
+ protected Double getProperty(Object bean, String name) {
+ try {
+ // Get the value of prop
+ String prop = "get" + name.substring(0,1).toUpperCase() + name.substring(1);
+ Method meth = bean.getClass().getMethod(prop, new Class[0]);
+ Object property = meth.invoke(bean, new Object[0]);
+ if (meth.getReturnType().equals(Double.TYPE)) {
+ return (Double) property;
+ } else if (meth.getReturnType().equals(Long.TYPE)) {
+ return Double.valueOf(((Long) property).doubleValue());
+ } else {
+ fail("wrong type: " + meth.getReturnType().getName());
+ }
+ } catch (NoSuchMethodException nsme) {
+ // ignored
+ } catch (InvocationTargetException ite) {
+ fail(ite.getMessage());
+ } catch (IllegalAccessException iae) {
+ fail(iae.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/data/LewTest.java b/src/test/java/org/apache/commons/math/stat/data/LewTest.java
new file mode 100644
index 0000000..2f72a82
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/data/LewTest.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.stat.data;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class LewTest extends CertifiedDataAbstractTest {
+
+ @Override
+ protected String getResourceName() {
+ return "org/apache/commons/math/stat/data/Lew.txt";
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/data/LotteryTest.java b/src/test/java/org/apache/commons/math/stat/data/LotteryTest.java
new file mode 100644
index 0000000..ad70dc8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/data/LotteryTest.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.stat.data;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class LotteryTest extends CertifiedDataAbstractTest {
+
+ @Override
+ protected String getResourceName() {
+ return "org/apache/commons/math/stat/data/Lottery.txt";
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatisticTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatisticTest.java
new file mode 100644
index 0000000..4d3159b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/AbstractUnivariateStatisticTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+
+/**
+ * Tests for AbstractUnivariateStatistic
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class AbstractUnivariateStatisticTest extends TestCase {
+
+ public AbstractUnivariateStatisticTest(String name) {
+ super(name);
+ }
+
+ protected double[] testArray = {0, 1, 2, 3, 4, 5};
+ protected double[] testWeightsArray = {0.3, 0.2, 1.3, 1.1, 1.0, 1.8};
+ protected double[] testNegativeWeightsArray = {-0.3, 0.2, -1.3, 1.1, 1.0, 1.8};
+ protected double[] nullArray = null;
+ protected double[] singletonArray = {0};
+ protected Mean testStatistic = new Mean();
+
+ public void testTestPositive() {
+ for (int j = 0; j < 6; j++) {
+ for (int i = 1; i < (7 - j); i++) {
+ assertTrue(testStatistic.test(testArray, 0, i));
+ }
+ }
+ assertTrue(testStatistic.test(singletonArray, 0, 1));
+ }
+
+ public void testTestNegative() {
+ assertFalse(testStatistic.test(singletonArray, 0, 0));
+ assertFalse(testStatistic.test(testArray, 0, 0));
+ try {
+ testStatistic.test(singletonArray, 2, 1); // start past end
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(testArray, 0, 7); // end past end
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(testArray, -1, 1); // start negative
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(testArray, 0, -1); // length negative
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(nullArray, 0, 1); // null array
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(testArray, nullArray, 0, 1); // null weights array
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(singletonArray, testWeightsArray, 0, 1); // weights.length != value.length
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.test(testArray, testNegativeWeightsArray, 0, 6); // can't have negative weights
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatisticsTest.java
new file mode 100644
index 0000000..d4e7a11
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/AggregateSummaryStatisticsTest.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.stat.descriptive;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.random.RandomData;
+import org.apache.commons.math.random.RandomDataImpl;
+
+
+/**
+ * Test cases for {@link AggregateSummaryStatistics}
+ *
+ */
+public class AggregateSummaryStatisticsTest extends TestCase {
+
+ /**
+ * Tests the standard aggregation behavior
+ */
+ public void testAggregation() {
+ AggregateSummaryStatistics aggregate = new AggregateSummaryStatistics();
+ SummaryStatistics setOneStats = aggregate.createContributingStatistics();
+ SummaryStatistics setTwoStats = aggregate.createContributingStatistics();
+
+ assertNotNull("The set one contributing stats are null", setOneStats);
+ assertNotNull("The set two contributing stats are null", setTwoStats);
+ assertNotSame("Contributing stats objects are the same", setOneStats, setTwoStats);
+
+ setOneStats.addValue(2);
+ setOneStats.addValue(3);
+ setOneStats.addValue(5);
+ setOneStats.addValue(7);
+ setOneStats.addValue(11);
+ assertEquals("Wrong number of set one values", 5, setOneStats.getN());
+ assertEquals("Wrong sum of set one values", 28.0, setOneStats.getSum());
+
+ setTwoStats.addValue(2);
+ setTwoStats.addValue(4);
+ setTwoStats.addValue(8);
+ assertEquals("Wrong number of set two values", 3, setTwoStats.getN());
+ assertEquals("Wrong sum of set two values", 14.0, setTwoStats.getSum());
+
+ assertEquals("Wrong number of aggregate values", 8, aggregate.getN());
+ assertEquals("Wrong aggregate sum", 42.0, aggregate.getSum());
+ }
+
+ /**
+ * Verify that aggregating over a partition gives the same results
+ * as direct computation.
+ *
+ * 1) Randomly generate a dataset of 10-100 values
+ * from [-100, 100]
+ * 2) Divide the dataset it into 2-5 partitions
+ * 3) Create an AggregateSummaryStatistic and ContributingStatistics
+ * for each partition
+ * 4) Compare results from the AggregateSummaryStatistic with values
+ * returned by a single SummaryStatistics instance that is provided
+ * the full dataset
+ */
+ public void testAggregationConsistency() throws Exception {
+
+ // Generate a random sample and random partition
+ double[] totalSample = generateSample();
+ double[][] subSamples = generatePartition(totalSample);
+ int nSamples = subSamples.length;
+
+ // Create aggregator and total stats for comparison
+ AggregateSummaryStatistics aggregate = new AggregateSummaryStatistics();
+ SummaryStatistics totalStats = new SummaryStatistics();
+
+ // Create array of component stats
+ SummaryStatistics componentStats[] = new SummaryStatistics[nSamples];
+
+ for (int i = 0; i < nSamples; i++) {
+
+ // Make componentStats[i] a contributing statistic to aggregate
+ componentStats[i] = aggregate.createContributingStatistics();
+
+ // Add values from subsample
+ for (int j = 0; j < subSamples[i].length; j++) {
+ componentStats[i].addValue(subSamples[i][j]);
+ }
+ }
+
+ // Compute totalStats directly
+ for (int i = 0; i < totalSample.length; i++) {
+ totalStats.addValue(totalSample[i]);
+ }
+
+ /*
+ * Compare statistics in totalStats with aggregate.
+ * Note that guaranteed success of this comparison depends on the
+ * fact that <aggregate> gets values in exactly the same order
+ * as <totalStats>.
+ *
+ */
+ assertEquals(totalStats.getSummary(), aggregate.getSummary());
+
+ }
+
+ /**
+ * Test aggregate function by randomly generating a dataset of 10-100 values
+ * from [-100, 100], dividing it into 2-5 partitions, computing stats for each
+ * partition and comparing the result of aggregate(...) applied to the collection
+ * of per-partition SummaryStatistics with a single SummaryStatistics computed
+ * over the full sample.
+ *
+ * @throws Exception
+ */
+ public void testAggregate() throws Exception {
+
+ // Generate a random sample and random partition
+ double[] totalSample = generateSample();
+ double[][] subSamples = generatePartition(totalSample);
+ int nSamples = subSamples.length;
+
+ // Compute combined stats directly
+ SummaryStatistics totalStats = new SummaryStatistics();
+ for (int i = 0; i < totalSample.length; i++) {
+ totalStats.addValue(totalSample[i]);
+ }
+
+ // Now compute subsample stats individually and aggregate
+ SummaryStatistics[] subSampleStats = new SummaryStatistics[nSamples];
+ for (int i = 0; i < nSamples; i++) {
+ subSampleStats[i] = new SummaryStatistics();
+ }
+ Collection<SummaryStatistics> aggregate = new ArrayList<SummaryStatistics>();
+ for (int i = 0; i < nSamples; i++) {
+ for (int j = 0; j < subSamples[i].length; j++) {
+ subSampleStats[i].addValue(subSamples[i][j]);
+ }
+ aggregate.add(subSampleStats[i]);
+ }
+
+ // Compare values
+ StatisticalSummary aggregatedStats = AggregateSummaryStatistics.aggregate(aggregate);
+ assertEquals(totalStats.getSummary(), aggregatedStats, 10E-12);
+ }
+
+
+ public void testAggregateDegenerate() throws Exception {
+ double[] totalSample = {1, 2, 3, 4, 5};
+ double[][] subSamples = {{1}, {2}, {3}, {4}, {5}};
+
+ // Compute combined stats directly
+ SummaryStatistics totalStats = new SummaryStatistics();
+ for (int i = 0; i < totalSample.length; i++) {
+ totalStats.addValue(totalSample[i]);
+ }
+
+ // Now compute subsample stats individually and aggregate
+ SummaryStatistics[] subSampleStats = new SummaryStatistics[5];
+ for (int i = 0; i < 5; i++) {
+ subSampleStats[i] = new SummaryStatistics();
+ }
+ Collection<SummaryStatistics> aggregate = new ArrayList<SummaryStatistics>();
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < subSamples[i].length; j++) {
+ subSampleStats[i].addValue(subSamples[i][j]);
+ }
+ aggregate.add(subSampleStats[i]);
+ }
+
+ // Compare values
+ StatisticalSummaryValues aggregatedStats = AggregateSummaryStatistics.aggregate(aggregate);
+ assertEquals(totalStats.getSummary(), aggregatedStats, 10E-12);
+ }
+
+ public void testAggregateSpecialValues() throws Exception {
+ double[] totalSample = {Double.POSITIVE_INFINITY, 2, 3, Double.NaN, 5};
+ double[][] subSamples = {{Double.POSITIVE_INFINITY, 2}, {3}, {Double.NaN}, {5}};
+
+ // Compute combined stats directly
+ SummaryStatistics totalStats = new SummaryStatistics();
+ for (int i = 0; i < totalSample.length; i++) {
+ totalStats.addValue(totalSample[i]);
+ }
+
+ // Now compute subsample stats individually and aggregate
+ SummaryStatistics[] subSampleStats = new SummaryStatistics[5];
+ for (int i = 0; i < 4; i++) {
+ subSampleStats[i] = new SummaryStatistics();
+ }
+ Collection<SummaryStatistics> aggregate = new ArrayList<SummaryStatistics>();
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < subSamples[i].length; j++) {
+ subSampleStats[i].addValue(subSamples[i][j]);
+ }
+ aggregate.add(subSampleStats[i]);
+ }
+
+ // Compare values
+ StatisticalSummaryValues aggregatedStats = AggregateSummaryStatistics.aggregate(aggregate);
+ assertEquals(totalStats.getSummary(), aggregatedStats, 10E-12);
+
+ }
+
+ /**
+ * Verifies that a StatisticalSummary and a StatisticalSummaryValues are equal up
+ * to delta, with NaNs, infinities returned in the same spots. For max, min, n, values
+ * have to agree exactly, delta is used only for sum, mean, variance, std dev.
+ */
+ protected static void assertEquals(StatisticalSummary expected, StatisticalSummary observed, double delta) {
+ TestUtils.assertEquals(expected.getMax(), observed.getMax(), 0);
+ TestUtils.assertEquals(expected.getMin(), observed.getMin(), 0);
+ assertEquals(expected.getN(), observed.getN());
+ TestUtils.assertEquals(expected.getSum(), observed.getSum(), delta);
+ TestUtils.assertEquals(expected.getMean(), observed.getMean(), delta);
+ TestUtils.assertEquals(expected.getStandardDeviation(), observed.getStandardDeviation(), delta);
+ TestUtils.assertEquals(expected.getVariance(), observed.getVariance(), delta);
+ }
+
+
+ /**
+ * Generates a random sample of double values.
+ * Sample size is random, between 10 and 100 and values are
+ * uniformly distributed over [-100, 100].
+ *
+ * @return array of random double values
+ */
+ private double[] generateSample() {
+ final RandomData randomData = new RandomDataImpl();
+ final int sampleSize = randomData.nextInt(10,100);
+ double[] out = new double[sampleSize];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = randomData.nextUniform(-100, 100);
+ }
+ return out;
+ }
+
+ /**
+ * Generates a partition of <sample> into up to 5 sequentially selected
+ * subsamples with randomly selected partition points.
+ *
+ * @param sample array to partition
+ * @return rectangular array with rows = subsamples
+ */
+ private double[][] generatePartition(double[] sample) {
+ final int length = sample.length;
+ final double[][] out = new double[5][];
+ final RandomData randomData = new RandomDataImpl();
+ int cur = 0;
+ int offset = 0;
+ int sampleCount = 0;
+ for (int i = 0; i < 5; i++) {
+ if (cur == length || offset == length) {
+ break;
+ }
+ final int next = (i == 4 || cur == length - 1) ? length - 1 : randomData.nextInt(cur, length - 1);
+ final int subLength = next - cur + 1;
+ out[i] = new double[subLength];
+ System.arraycopy(sample, offset, out[i], 0, subLength);
+ cur = next + 1;
+ sampleCount++;
+ offset += subLength;
+ }
+ if (sampleCount < 5) {
+ double[][] out2 = new double[sampleCount][];
+ for (int j = 0; j < sampleCount; j++) {
+ final int curSize = out[j].length;
+ out2[j] = new double[curSize];
+ System.arraycopy(out[j], 0, out2[j], 0, curSize);
+ }
+ return out2;
+ } else {
+ return out;
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsTest.java
new file mode 100644
index 0000000..c86081f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/DescriptiveStatisticsTest.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
+ * or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.stat.descriptive.rank.Percentile;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Test cases for the DescriptiveStatistics class.
+ *
+ * @version $Revision: 1003907 $ $Date: 2007-08-16 15:36:33 -0500 (Thu, 16 Aug
+ * 2007) $
+ */
+public class DescriptiveStatisticsTest extends TestCase {
+
+ public DescriptiveStatisticsTest(String name) {
+ super(name);
+ }
+
+ protected DescriptiveStatistics createDescriptiveStatistics() {
+ return new DescriptiveStatistics();
+ }
+
+ public void testSetterInjection() {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ stats.addValue(1);
+ stats.addValue(3);
+ assertEquals(2, stats.getMean(), 1E-10);
+ // Now lets try some new math
+ stats.setMeanImpl(new deepMean());
+ assertEquals(42, stats.getMean(), 1E-10);
+ }
+
+ public void testCopy() {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ stats.addValue(1);
+ stats.addValue(3);
+ DescriptiveStatistics copy = new DescriptiveStatistics(stats);
+ assertEquals(2, copy.getMean(), 1E-10);
+ // Now lets try some new math
+ stats.setMeanImpl(new deepMean());
+ copy = stats.copy();
+ assertEquals(42, copy.getMean(), 1E-10);
+ }
+
+ public void testWindowSize() {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ stats.setWindowSize(300);
+ for (int i = 0; i < 100; ++i) {
+ stats.addValue(i + 1);
+ }
+ int refSum = (100 * 101) / 2;
+ assertEquals(refSum / 100.0, stats.getMean(), 1E-10);
+ assertEquals(300, stats.getWindowSize());
+ try {
+ stats.setWindowSize(-3);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ assertEquals(300, stats.getWindowSize());
+ stats.setWindowSize(50);
+ assertEquals(50, stats.getWindowSize());
+ int refSum2 = refSum - (50 * 51) / 2;
+ assertEquals(refSum2 / 50.0, stats.getMean(), 1E-10);
+ }
+
+ public void testGetValues() {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ for (int i = 100; i > 0; --i) {
+ stats.addValue(i);
+ }
+ int refSum = (100 * 101) / 2;
+ assertEquals(refSum / 100.0, stats.getMean(), 1E-10);
+ double[] v = stats.getValues();
+ for (int i = 0; i < v.length; ++i) {
+ assertEquals(100.0 - i, v[i], 1.0e-10);
+ }
+ double[] s = stats.getSortedValues();
+ for (int i = 0; i < s.length; ++i) {
+ assertEquals(i + 1.0, s[i], 1.0e-10);
+ }
+ assertEquals(12.0, stats.getElement(88), 1.0e-10);
+ }
+
+ public void testToString() {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ stats.addValue(1);
+ stats.addValue(2);
+ stats.addValue(3);
+ Locale d = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ assertEquals("DescriptiveStatistics:\n" +
+ "n: 3\n" +
+ "min: 1.0\n" +
+ "max: 3.0\n" +
+ "mean: 2.0\n" +
+ "std dev: 1.0\n" +
+ "median: 2.0\n" +
+ "skewness: 0.0\n" +
+ "kurtosis: NaN\n", stats.toString());
+ Locale.setDefault(d);
+ }
+
+ public void testShuffledStatistics() {
+ // the purpose of this test is only to check the get/set methods
+ // we are aware shuffling statistics like this is really not
+ // something sensible to do in production ...
+ DescriptiveStatistics reference = createDescriptiveStatistics();
+ DescriptiveStatistics shuffled = createDescriptiveStatistics();
+
+ UnivariateStatistic tmp = shuffled.getGeometricMeanImpl();
+ shuffled.setGeometricMeanImpl(shuffled.getMeanImpl());
+ shuffled.setMeanImpl(shuffled.getKurtosisImpl());
+ shuffled.setKurtosisImpl(shuffled.getSkewnessImpl());
+ shuffled.setSkewnessImpl(shuffled.getVarianceImpl());
+ shuffled.setVarianceImpl(shuffled.getMaxImpl());
+ shuffled.setMaxImpl(shuffled.getMinImpl());
+ shuffled.setMinImpl(shuffled.getSumImpl());
+ shuffled.setSumImpl(shuffled.getSumsqImpl());
+ shuffled.setSumsqImpl(tmp);
+
+ for (int i = 100; i > 0; --i) {
+ reference.addValue(i);
+ shuffled.addValue(i);
+ }
+
+ assertEquals(reference.getMean(), shuffled.getGeometricMean(), 1.0e-10);
+ assertEquals(reference.getKurtosis(), shuffled.getMean(), 1.0e-10);
+ assertEquals(reference.getSkewness(), shuffled.getKurtosis(), 1.0e-10);
+ assertEquals(reference.getVariance(), shuffled.getSkewness(), 1.0e-10);
+ assertEquals(reference.getMax(), shuffled.getVariance(), 1.0e-10);
+ assertEquals(reference.getMin(), shuffled.getMax(), 1.0e-10);
+ assertEquals(reference.getSum(), shuffled.getMin(), 1.0e-10);
+ assertEquals(reference.getSumsq(), shuffled.getSum(), 1.0e-10);
+ assertEquals(reference.getGeometricMean(), shuffled.getSumsq(), 1.0e-10);
+
+ }
+
+ public void testPercentileSetter() throws Exception {
+ DescriptiveStatistics stats = createDescriptiveStatistics();
+ stats.addValue(1);
+ stats.addValue(2);
+ stats.addValue(3);
+ assertEquals(2, stats.getPercentile(50.0), 1E-10);
+
+ // Inject wrapped Percentile impl
+ stats.setPercentileImpl(new goodPercentile());
+ assertEquals(2, stats.getPercentile(50.0), 1E-10);
+
+ // Try "new math" impl
+ stats.setPercentileImpl(new subPercentile());
+ assertEquals(10.0, stats.getPercentile(10.0), 1E-10);
+
+ // Try to set bad impl
+ try {
+ stats.setPercentileImpl(new badPercentile());
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void test20090720() {
+ DescriptiveStatistics descriptiveStatistics = new DescriptiveStatistics(100);
+ for (int i = 0; i < 161; i++) {
+ descriptiveStatistics.addValue(1.2);
+ }
+ descriptiveStatistics.clear();
+ descriptiveStatistics.addValue(1.2);
+ assertEquals(1, descriptiveStatistics.getN());
+ }
+
+ public void testRemoval() {
+
+ final DescriptiveStatistics dstat = createDescriptiveStatistics();
+
+ checkremoval(dstat, 1, 6.0, 0.0, Double.NaN);
+ checkremoval(dstat, 3, 5.0, 3.0, 4.5);
+ checkremoval(dstat, 6, 3.5, 2.5, 3.0);
+ checkremoval(dstat, 9, 3.5, 2.5, 3.0);
+ checkremoval(dstat, DescriptiveStatistics.INFINITE_WINDOW, 3.5, 2.5, 3.0);
+
+ }
+
+ public void checkremoval(DescriptiveStatistics dstat, int wsize,
+ double mean1, double mean2, double mean3) {
+
+ dstat.setWindowSize(wsize);
+ dstat.clear();
+
+ for (int i = 1 ; i <= 6 ; ++i) {
+ dstat.addValue(i);
+ }
+
+ assertTrue(MathUtils.equalsIncludingNaN(mean1, dstat.getMean()));
+ dstat.replaceMostRecentValue(0);
+ assertTrue(MathUtils.equalsIncludingNaN(mean2, dstat.getMean()));
+ dstat.removeMostRecentValue();
+ assertTrue(MathUtils.equalsIncludingNaN(mean3, dstat.getMean()));
+
+ }
+
+ // Test UnivariateStatistics impls for setter injection tests
+
+ /**
+ * A new way to compute the mean
+ */
+ static class deepMean implements UnivariateStatistic {
+
+ public double evaluate(double[] values, int begin, int length) {
+ return 42;
+ }
+
+ public double evaluate(double[] values) {
+ return 42;
+ }
+ public UnivariateStatistic copy() {
+ return new deepMean();
+ }
+ }
+
+ /**
+ * Test percentile implementation - wraps a Percentile
+ */
+ static class goodPercentile implements UnivariateStatistic {
+ private Percentile percentile = new Percentile();
+ public void setQuantile(double quantile) {
+ percentile.setQuantile(quantile);
+ }
+ public double evaluate(double[] values, int begin, int length) {
+ return percentile.evaluate(values, begin, length);
+ }
+ public double evaluate(double[] values) {
+ return percentile.evaluate(values);
+ }
+ public UnivariateStatistic copy() {
+ goodPercentile result = new goodPercentile();
+ result.setQuantile(percentile.getQuantile());
+ return result;
+ }
+ }
+
+ /**
+ * Test percentile subclass - another "new math" impl
+ * Always returns currently set quantile
+ */
+ static class subPercentile extends Percentile {
+ @Override
+ public double evaluate(double[] values, int begin, int length) {
+ return getQuantile();
+ }
+ @Override
+ public double evaluate(double[] values) {
+ return getQuantile();
+ }
+ private static final long serialVersionUID = 8040701391045914979L;
+ @Override
+ public Percentile copy() {
+ subPercentile result = new subPercentile();
+ return result;
+ }
+ }
+
+ /**
+ * "Bad" test percentile implementation - no setQuantile
+ */
+ static class badPercentile implements UnivariateStatistic {
+ private Percentile percentile = new Percentile();
+ public double evaluate(double[] values, int begin, int length) {
+ return percentile.evaluate(values, begin, length);
+ }
+ public double evaluate(double[] values) {
+ return percentile.evaluate(values);
+ }
+ public UnivariateStatistic copy() {
+ return new badPercentile();
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/InteractionTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/InteractionTest.java
new file mode 100644
index 0000000..6d51997
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/InteractionTest.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.stat.descriptive;
+
+import org.apache.commons.math.stat.descriptive.moment.FourthMoment;
+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 junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class InteractionTest extends TestCase {
+
+ protected double mean = 12.40454545454550;
+ protected double var = 10.00235930735930;
+ protected double skew = 1.437423729196190;
+ protected double kurt = 2.377191264804700;
+
+ protected double tolerance = 10E-12;
+
+ protected double[] testArray =
+ {
+ 12.5,
+ 12,
+ 11.8,
+ 14.2,
+ 14.9,
+ 14.5,
+ 21,
+ 8.2,
+ 10.3,
+ 11.3,
+ 14.1,
+ 9.9,
+ 12.2,
+ 12,
+ 12.1,
+ 11,
+ 19.8,
+ 11,
+ 10,
+ 8.8,
+ 9,
+ 12.3 };
+
+ public InteractionTest(String name) {
+ super(name);
+ }
+
+
+ public void testInteraction() {
+
+ FourthMoment m4 = new FourthMoment();
+ Mean m = new Mean(m4);
+ Variance v = new Variance(m4);
+ Skewness s= new Skewness(m4);
+ Kurtosis k = new Kurtosis(m4);
+
+ for (int i = 0; i < testArray.length; i++){
+ m4.increment(testArray[i]);
+ }
+
+ assertEquals(mean,m.getResult(),tolerance);
+ assertEquals(var,v.getResult(),tolerance);
+ assertEquals(skew ,s.getResult(),tolerance);
+ assertEquals(kurt,k.getResult(),tolerance);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImpl.java b/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImpl.java
new file mode 100644
index 0000000..a74de61
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImpl.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;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math.util.DefaultTransformer;
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.NumberTransformer;
+
+/**
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class ListUnivariateImpl extends DescriptiveStatistics implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8837442489133392138L;
+
+ /**
+ * Holds a reference to a list - GENERICs are going to make
+ * our lives easier here as we could only accept List<Number>
+ */
+ protected List<Object> list;
+
+ /** Number Transformer maps Objects to Number for us. */
+ protected NumberTransformer transformer;
+
+ /**
+ * No argument Constructor
+ */
+ public ListUnivariateImpl(){
+ this(new ArrayList<Object>());
+ }
+
+ /**
+ * Construct a ListUnivariate with a specific List.
+ * @param list The list that will back this DescriptiveStatistics
+ */
+ public ListUnivariateImpl(List<Object> list) {
+ this(list, new DefaultTransformer());
+ }
+
+ /**
+ * Construct a ListUnivariate with a specific List.
+ * @param list The list that will back this DescriptiveStatistics
+ * @param transformer the number transformer used to convert the list items.
+ */
+ public ListUnivariateImpl(List<Object> list, NumberTransformer transformer) {
+ super();
+ this.list = list;
+ this.transformer = transformer;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getValues() {
+
+ int length = list.size();
+
+ // If the window size is not INFINITE_WINDOW AND
+ // the current list is larger that the window size, we need to
+ // take into account only the last n elements of the list
+ // as definied by windowSize
+
+ if (windowSize != DescriptiveStatistics.INFINITE_WINDOW &&
+ windowSize < list.size())
+ {
+ length = list.size() - FastMath.max(0, list.size() - windowSize);
+ }
+
+ // Create an array to hold all values
+ double[] copiedArray = new double[length];
+
+ for (int i = 0; i < copiedArray.length; i++) {
+ copiedArray[i] = getElement(i);
+ }
+ return copiedArray;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getElement(int index) {
+
+ double value = Double.NaN;
+
+ int calcIndex = index;
+
+ if (windowSize != DescriptiveStatistics.INFINITE_WINDOW &&
+ windowSize < list.size())
+ {
+ calcIndex = (list.size() - windowSize) + index;
+ }
+
+
+ try {
+ value = transformer.transform(list.get(calcIndex));
+ } catch (MathException e) {
+ e.printStackTrace();
+ }
+
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long getN() {
+ int n = 0;
+
+ if (windowSize != DescriptiveStatistics.INFINITE_WINDOW) {
+ if (list.size() > windowSize) {
+ n = windowSize;
+ } else {
+ n = list.size();
+ }
+ } else {
+ n = list.size();
+ }
+ return n;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addValue(double v) {
+ list.add(Double.valueOf(v));
+ }
+
+ /**
+ * Adds an object to this list.
+ * @param o Object to add to the list
+ */
+ public void addObject(Object o) {
+ list.add(o);
+ }
+
+ /**
+ * Clears all statistics.
+ * <p>
+ * <strong>N.B.: </strong> This method has the side effect of clearing the underlying list.
+ */
+ @Override
+ public void clear() {
+ list.clear();
+ }
+
+ /**
+ * Apply the given statistic to this univariate collection.
+ * @param stat the statistic to apply
+ * @return the computed value of the statistic.
+ */
+ @Override
+ public double apply(UnivariateStatistic stat) {
+ double[] v = this.getValues();
+
+ if (v != null) {
+ return stat.evaluate(v, 0, v.length);
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Access the number transformer.
+ * @return the number transformer.
+ */
+ public NumberTransformer getTransformer() {
+ return transformer;
+ }
+
+ /**
+ * Modify the number transformer.
+ * @param transformer the new number transformer.
+ */
+ public void setTransformer(NumberTransformer transformer) {
+ this.transformer = transformer;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public synchronized void setWindowSize(int windowSize) {
+ this.windowSize = windowSize;
+ //Discard elements from the front of the list if the windowSize is less than
+ // the size of the list.
+ int extra = list.size() - windowSize;
+ for (int i = 0; i < extra; i++) {
+ list.remove(0);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public synchronized int getWindowSize() {
+ return windowSize;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImplTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImplTest.java
new file mode 100644
index 0000000..17f041b
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/ListUnivariateImplTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link ListUnivariateImpl} class.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+
+public final class ListUnivariateImplTest extends TestCase {
+
+ private double one = 1;
+ private float two = 2;
+ private int three = 3;
+
+ private double mean = 2;
+ private double sumSq = 18;
+ private double sum = 8;
+ private double var = 0.666666666666666666667;
+ private double std = FastMath.sqrt(var);
+ private double n = 4;
+ private double min = 1;
+ private double max = 3;
+ private double tolerance = 10E-15;
+
+ public ListUnivariateImplTest(String name) {
+ super(name);
+ }
+
+ /** test stats */
+ public void testStats() {
+ List<Object> externalList = new ArrayList<Object>();
+
+ DescriptiveStatistics u = new ListUnivariateImpl( externalList );
+
+ assertEquals("total count",0,u.getN(),tolerance);
+ u.addValue(one);
+ u.addValue(two);
+ u.addValue(two);
+ u.addValue(three);
+ assertEquals("N",n,u.getN(),tolerance);
+ assertEquals("sum",sum,u.getSum(),tolerance);
+ assertEquals("sumsq",sumSq,u.getSumsq(),tolerance);
+ assertEquals("var",var,u.getVariance(),tolerance);
+ assertEquals("std",std,u.getStandardDeviation(),tolerance);
+ assertEquals("mean",mean,u.getMean(),tolerance);
+ assertEquals("min",min,u.getMin(),tolerance);
+ assertEquals("max",max,u.getMax(),tolerance);
+ u.clear();
+ assertEquals("total count",0,u.getN(),tolerance);
+ }
+
+ public void testN0andN1Conditions() throws Exception {
+ List<Object> list = new ArrayList<Object>();
+
+ DescriptiveStatistics u = new ListUnivariateImpl( list );
+
+ assertTrue("Mean of n = 0 set should be NaN", Double.isNaN( u.getMean() ) );
+ assertTrue("Standard Deviation of n = 0 set should be NaN", Double.isNaN( u.getStandardDeviation() ) );
+ assertTrue("Variance of n = 0 set should be NaN", Double.isNaN(u.getVariance() ) );
+
+ list.add( Double.valueOf(one));
+
+ assertTrue( "Mean of n = 1 set should be value of single item n1", u.getMean() == one);
+ assertTrue( "StdDev of n = 1 set should be zero, instead it is: " + u.getStandardDeviation(), u.getStandardDeviation() == 0);
+ assertTrue( "Variance of n = 1 set should be zero", u.getVariance() == 0);
+ }
+
+ public void testSkewAndKurtosis() {
+ DescriptiveStatistics u = new DescriptiveStatistics();
+
+ double[] testArray = { 12.5, 12, 11.8, 14.2, 14.9, 14.5, 21, 8.2, 10.3, 11.3, 14.1,
+ 9.9, 12.2, 12, 12.1, 11, 19.8, 11, 10, 8.8, 9, 12.3 };
+ for( int i = 0; i < testArray.length; i++) {
+ u.addValue( testArray[i]);
+ }
+
+ assertEquals("mean", 12.40455, u.getMean(), 0.0001);
+ assertEquals("variance", 10.00236, u.getVariance(), 0.0001);
+ assertEquals("skewness", 1.437424, u.getSkewness(), 0.0001);
+ assertEquals("kurtosis", 2.37719, u.getKurtosis(), 0.0001);
+ }
+
+ public void testProductAndGeometricMean() throws Exception {
+ ListUnivariateImpl u = new ListUnivariateImpl(new ArrayList<Object>());
+ u.setWindowSize(10);
+
+ u.addValue( 1.0 );
+ u.addValue( 2.0 );
+ u.addValue( 3.0 );
+ u.addValue( 4.0 );
+
+ assertEquals( "Geometric mean not expected", 2.213364, u.getGeometricMean(), 0.00001 );
+
+ // Now test rolling - StorelessDescriptiveStatistics should discount the contribution
+ // of a discarded element
+ for( int i = 0; i < 10; i++ ) {
+ u.addValue( i + 2 );
+ }
+ // Values should be (2,3,4,5,6,7,8,9,10,11)
+
+ assertEquals( "Geometric mean not expected", 5.755931, u.getGeometricMean(), 0.00001 );
+
+
+ }
+
+ /** test stats */
+ public void testSerialization() {
+
+ DescriptiveStatistics u = new ListUnivariateImpl();
+
+ assertEquals("total count",0,u.getN(),tolerance);
+ u.addValue(one);
+ u.addValue(two);
+
+ DescriptiveStatistics u2 = (DescriptiveStatistics)TestUtils.serializeAndRecover(u);
+
+ u2.addValue(two);
+ u2.addValue(three);
+
+ assertEquals("N",n,u2.getN(),tolerance);
+ assertEquals("sum",sum,u2.getSum(),tolerance);
+ assertEquals("sumsq",sumSq,u2.getSumsq(),tolerance);
+ assertEquals("var",var,u2.getVariance(),tolerance);
+ assertEquals("std",std,u2.getStandardDeviation(),tolerance);
+ assertEquals("mean",mean,u2.getMean(),tolerance);
+ assertEquals("min",min,u2.getMin(),tolerance);
+ assertEquals("max",max,u2.getMax(),tolerance);
+
+ u2.clear();
+ assertEquals("total count",0,u2.getN(),tolerance);
+ }
+}
+
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/MixedListUnivariateImplTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/MixedListUnivariateImplTest.java
new file mode 100644
index 0000000..6d24945
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/MixedListUnivariateImplTest.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.stat.descriptive;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.util.FastMath;
+import org.apache.commons.math.util.NumberTransformer;
+import org.apache.commons.math.util.TransformerMap;
+
+/**
+ * Test cases for the {@link ListUnivariateImpl} class.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+
+public final class MixedListUnivariateImplTest extends TestCase {
+ private double one = 1;
+ private float two = 2;
+ private int three = 3;
+
+ private double mean = 2;
+ private double sumSq = 18;
+ private double sum = 8;
+ private double var = 0.666666666666666666667;
+ private double std = FastMath.sqrt(var);
+ private double n = 4;
+ private double min = 1;
+ private double max = 3;
+ private double tolerance = 10E-15;
+
+ private TransformerMap transformers = new TransformerMap();
+
+ public MixedListUnivariateImplTest(String name) {
+ super(name);
+ transformers = new TransformerMap();
+
+ transformers.putTransformer(Foo.class, new FooTransformer());
+
+ transformers.putTransformer(Bar.class, new BarTransformer());
+
+ }
+
+ /** test stats */
+ public void testStats() {
+ List<Object> externalList = new ArrayList<Object>();
+
+ DescriptiveStatistics u = new ListUnivariateImpl(externalList,transformers);
+
+ assertEquals("total count", 0, u.getN(), tolerance);
+ u.addValue(one);
+ u.addValue(two);
+ u.addValue(two);
+ u.addValue(three);
+ assertEquals("N", n, u.getN(), tolerance);
+ assertEquals("sum", sum, u.getSum(), tolerance);
+ assertEquals("sumsq", sumSq, u.getSumsq(), tolerance);
+ assertEquals("var", var, u.getVariance(), tolerance);
+ assertEquals("std", std, u.getStandardDeviation(), tolerance);
+ assertEquals("mean", mean, u.getMean(), tolerance);
+ assertEquals("min", min, u.getMin(), tolerance);
+ assertEquals("max", max, u.getMax(), tolerance);
+ u.clear();
+ assertEquals("total count", 0, u.getN(), tolerance);
+ }
+
+ public void testN0andN1Conditions() throws Exception {
+ DescriptiveStatistics u = new ListUnivariateImpl(new ArrayList<Object>(),transformers);
+
+ assertTrue(
+ "Mean of n = 0 set should be NaN",
+ Double.isNaN(u.getMean()));
+ assertTrue(
+ "Standard Deviation of n = 0 set should be NaN",
+ Double.isNaN(u.getStandardDeviation()));
+ assertTrue(
+ "Variance of n = 0 set should be NaN",
+ Double.isNaN(u.getVariance()));
+
+ u.addValue(one);
+
+ assertTrue(
+ "Mean of n = 1 set should be value of single item n1, instead it is " + u.getMean() ,
+ u.getMean() == one);
+
+ assertTrue(
+ "StdDev of n = 1 set should be zero, instead it is: "
+ + u.getStandardDeviation(),
+ u.getStandardDeviation() == 0);
+ assertTrue(
+ "Variance of n = 1 set should be zero",
+ u.getVariance() == 0);
+ }
+
+ public void testSkewAndKurtosis() {
+ ListUnivariateImpl u =
+ new ListUnivariateImpl(new ArrayList<Object>(), transformers);
+
+ u.addObject("12.5");
+ u.addObject(Integer.valueOf(12));
+ u.addObject("11.8");
+ u.addObject("14.2");
+ u.addObject(new Foo());
+ u.addObject("14.5");
+ u.addObject(Long.valueOf(21));
+ u.addObject("8.2");
+ u.addObject("10.3");
+ u.addObject("11.3");
+ u.addObject(Float.valueOf(14.1f));
+ u.addObject("9.9");
+ u.addObject("12.2");
+ u.addObject(new Bar());
+ u.addObject("12.1");
+ u.addObject("11");
+ u.addObject(Double.valueOf(19.8));
+ u.addObject("11");
+ u.addObject("10");
+ u.addObject("8.8");
+ u.addObject("9");
+ u.addObject("12.3");
+
+
+ assertEquals("mean", 12.40455, u.getMean(), 0.0001);
+ assertEquals("variance", 10.00236, u.getVariance(), 0.0001);
+ assertEquals("skewness", 1.437424, u.getSkewness(), 0.0001);
+ assertEquals("kurtosis", 2.37719, u.getKurtosis(), 0.0001);
+ }
+
+ public void testProductAndGeometricMean() throws Exception {
+ ListUnivariateImpl u = new ListUnivariateImpl(new ArrayList<Object>(),transformers);
+ u.setWindowSize(10);
+
+ u.addValue(1.0);
+ u.addValue(2.0);
+ u.addValue(3.0);
+ u.addValue(4.0);
+
+ assertEquals(
+ "Geometric mean not expected",
+ 2.213364,
+ u.getGeometricMean(),
+ 0.00001);
+
+ // Now test rolling - StorelessDescriptiveStatistics should discount the contribution
+ // of a discarded element
+ for (int i = 0; i < 10; i++) {
+ u.addValue(i + 2);
+ }
+ // Values should be (2,3,4,5,6,7,8,9,10,11)
+ assertEquals(
+ "Geometric mean not expected",
+ 5.755931,
+ u.getGeometricMean(),
+ 0.00001);
+
+ }
+
+ public static final class Foo {
+ public String heresFoo() {
+ return "14.9";
+ }
+ }
+
+ public static final class FooTransformer implements NumberTransformer, Serializable {
+ private static final long serialVersionUID = -4252248129291326127L;
+ public double transform(Object o) {
+ return Double.parseDouble(((Foo) o).heresFoo());
+ }
+ }
+
+ public static final class Bar {
+ public String heresBar() {
+ return "12.0";
+ }
+ }
+
+ public static final class BarTransformer implements NumberTransformer, Serializable {
+ private static final long serialVersionUID = -1768345377764262043L;
+ public double transform(Object o) {
+ return Double.parseDouble(((Bar) o).heresBar());
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatisticsTest.java
new file mode 100644
index 0000000..bac72c3
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/MultivariateSummaryStatisticsTest.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link MultivariateSummaryStatistics} class.
+ *
+ * @version $Revision: 1003907 $ $Date: 2010-10-03 00:23:34 +0200 (dim. 03 oct. 2010) $
+ */
+
+public class MultivariateSummaryStatisticsTest extends TestCase {
+
+ public MultivariateSummaryStatisticsTest(String name) {
+ super(name);
+ }
+
+ protected MultivariateSummaryStatistics createMultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
+ return new MultivariateSummaryStatistics(k, isCovarianceBiasCorrected);
+ }
+
+ public void testSetterInjection() throws Exception {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(2, true);
+ u.setMeanImpl(new StorelessUnivariateStatistic[] {
+ new sumMean(), new sumMean()
+ });
+ u.addValue(new double[] { 1, 2 });
+ u.addValue(new double[] { 3, 4 });
+ assertEquals(4, u.getMean()[0], 1E-14);
+ assertEquals(6, u.getMean()[1], 1E-14);
+ u.clear();
+ u.addValue(new double[] { 1, 2 });
+ u.addValue(new double[] { 3, 4 });
+ assertEquals(4, u.getMean()[0], 1E-14);
+ assertEquals(6, u.getMean()[1], 1E-14);
+ u.clear();
+ u.setMeanImpl(new StorelessUnivariateStatistic[] {
+ new Mean(), new Mean()
+ }); // OK after clear
+ u.addValue(new double[] { 1, 2 });
+ u.addValue(new double[] { 3, 4 });
+ assertEquals(2, u.getMean()[0], 1E-14);
+ assertEquals(3, u.getMean()[1], 1E-14);
+ assertEquals(2, u.getDimension());
+ }
+
+ public void testSetterIllegalState() throws Exception {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(2, true);
+ u.addValue(new double[] { 1, 2 });
+ u.addValue(new double[] { 3, 4 });
+ try {
+ u.setMeanImpl(new StorelessUnivariateStatistic[] {
+ new sumMean(), new sumMean()
+ });
+ fail("Expecting IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ public void testToString() throws DimensionMismatchException {
+ MultivariateSummaryStatistics stats = createMultivariateSummaryStatistics(2, true);
+ stats.addValue(new double[] {1, 3});
+ stats.addValue(new double[] {2, 2});
+ stats.addValue(new double[] {3, 1});
+ Locale d = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ final String suffix = System.getProperty("line.separator");
+ assertEquals("MultivariateSummaryStatistics:" + suffix+
+ "n: 3" +suffix+
+ "min: 1.0, 1.0" +suffix+
+ "max: 3.0, 3.0" +suffix+
+ "mean: 2.0, 2.0" +suffix+
+ "geometric mean: 1.817..., 1.817..." +suffix+
+ "sum of squares: 14.0, 14.0" +suffix+
+ "sum of logarithms: 1.791..., 1.791..." +suffix+
+ "standard deviation: 1.0, 1.0" +suffix+
+ "covariance: Array2DRowRealMatrix{{1.0,-1.0},{-1.0,1.0}}" +suffix,
+ stats.toString().replaceAll("([0-9]+\\.[0-9][0-9][0-9])[0-9]+", "$1..."));
+ Locale.setDefault(d);
+ }
+
+ public void testShuffledStatistics() throws DimensionMismatchException {
+ // the purpose of this test is only to check the get/set methods
+ // we are aware shuffling statistics like this is really not
+ // something sensible to do in production ...
+ MultivariateSummaryStatistics reference = createMultivariateSummaryStatistics(2, true);
+ MultivariateSummaryStatistics shuffled = createMultivariateSummaryStatistics(2, true);
+
+ StorelessUnivariateStatistic[] tmp = shuffled.getGeoMeanImpl();
+ shuffled.setGeoMeanImpl(shuffled.getMeanImpl());
+ shuffled.setMeanImpl(shuffled.getMaxImpl());
+ shuffled.setMaxImpl(shuffled.getMinImpl());
+ shuffled.setMinImpl(shuffled.getSumImpl());
+ shuffled.setSumImpl(shuffled.getSumsqImpl());
+ shuffled.setSumsqImpl(shuffled.getSumLogImpl());
+ shuffled.setSumLogImpl(tmp);
+
+ for (int i = 100; i > 0; --i) {
+ reference.addValue(new double[] {i, i});
+ shuffled.addValue(new double[] {i, i});
+ }
+
+ TestUtils.assertEquals(reference.getMean(), shuffled.getGeometricMean(), 1.0e-10);
+ TestUtils.assertEquals(reference.getMax(), shuffled.getMean(), 1.0e-10);
+ TestUtils.assertEquals(reference.getMin(), shuffled.getMax(), 1.0e-10);
+ TestUtils.assertEquals(reference.getSum(), shuffled.getMin(), 1.0e-10);
+ TestUtils.assertEquals(reference.getSumSq(), shuffled.getSum(), 1.0e-10);
+ TestUtils.assertEquals(reference.getSumLog(), shuffled.getSumSq(), 1.0e-10);
+ TestUtils.assertEquals(reference.getGeometricMean(), shuffled.getSumLog(), 1.0e-10);
+
+ }
+
+ /**
+ * Bogus mean implementation to test setter injection.
+ * Returns the sum instead of the mean.
+ */
+ static class sumMean implements StorelessUnivariateStatistic {
+ private double sum = 0;
+ private long n = 0;
+ public double evaluate(double[] values, int begin, int length) {
+ return 0;
+ }
+ public double evaluate(double[] values) {
+ return 0;
+ }
+ public void clear() {
+ sum = 0;
+ n = 0;
+ }
+ public long getN() {
+ return n;
+ }
+ public double getResult() {
+ return sum;
+ }
+ public void increment(double d) {
+ sum += d;
+ n++;
+ }
+ public void incrementAll(double[] values, int start, int length) {
+ }
+ public void incrementAll(double[] values) {
+ }
+ public StorelessUnivariateStatistic copy() {
+ return new sumMean();
+ }
+ }
+
+ public void testDimension() {
+ try {
+ createMultivariateSummaryStatistics(2, true).addValue(new double[3]);
+ fail("Expecting DimensionMismatchException");
+ } catch (DimensionMismatchException dme) {
+ // expected behavior
+ }
+ }
+
+ /** test stats */
+ public void testStats() throws DimensionMismatchException {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(2, true);
+ assertEquals(0, u.getN());
+ u.addValue(new double[] { 1, 2 });
+ u.addValue(new double[] { 2, 3 });
+ u.addValue(new double[] { 2, 3 });
+ u.addValue(new double[] { 3, 4 });
+ assertEquals( 4, u.getN());
+ assertEquals( 8, u.getSum()[0], 1.0e-10);
+ assertEquals(12, u.getSum()[1], 1.0e-10);
+ assertEquals(18, u.getSumSq()[0], 1.0e-10);
+ assertEquals(38, u.getSumSq()[1], 1.0e-10);
+ assertEquals( 1, u.getMin()[0], 1.0e-10);
+ assertEquals( 2, u.getMin()[1], 1.0e-10);
+ assertEquals( 3, u.getMax()[0], 1.0e-10);
+ assertEquals( 4, u.getMax()[1], 1.0e-10);
+ assertEquals(2.4849066497880003102, u.getSumLog()[0], 1.0e-10);
+ assertEquals( 4.276666119016055311, u.getSumLog()[1], 1.0e-10);
+ assertEquals( 1.8612097182041991979, u.getGeometricMean()[0], 1.0e-10);
+ assertEquals( 2.9129506302439405217, u.getGeometricMean()[1], 1.0e-10);
+ assertEquals( 2, u.getMean()[0], 1.0e-10);
+ assertEquals( 3, u.getMean()[1], 1.0e-10);
+ assertEquals(FastMath.sqrt(2.0 / 3.0), u.getStandardDeviation()[0], 1.0e-10);
+ assertEquals(FastMath.sqrt(2.0 / 3.0), u.getStandardDeviation()[1], 1.0e-10);
+ assertEquals(2.0 / 3.0, u.getCovariance().getEntry(0, 0), 1.0e-10);
+ assertEquals(2.0 / 3.0, u.getCovariance().getEntry(0, 1), 1.0e-10);
+ assertEquals(2.0 / 3.0, u.getCovariance().getEntry(1, 0), 1.0e-10);
+ assertEquals(2.0 / 3.0, u.getCovariance().getEntry(1, 1), 1.0e-10);
+ u.clear();
+ assertEquals(0, u.getN());
+ }
+
+ public void testN0andN1Conditions() throws Exception {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(1, true);
+ assertTrue(Double.isNaN(u.getMean()[0]));
+ assertTrue(Double.isNaN(u.getStandardDeviation()[0]));
+
+ /* n=1 */
+ u.addValue(new double[] { 1 });
+ assertEquals(1.0, u.getMean()[0], 1.0e-10);
+ assertEquals(1.0, u.getGeometricMean()[0], 1.0e-10);
+ assertEquals(0.0, u.getStandardDeviation()[0], 1.0e-10);
+
+ /* n=2 */
+ u.addValue(new double[] { 2 });
+ assertTrue(u.getStandardDeviation()[0] > 0);
+
+ }
+
+ public void testNaNContracts() throws DimensionMismatchException {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(1, true);
+ assertTrue(Double.isNaN(u.getMean()[0]));
+ assertTrue(Double.isNaN(u.getMin()[0]));
+ assertTrue(Double.isNaN(u.getStandardDeviation()[0]));
+ assertTrue(Double.isNaN(u.getGeometricMean()[0]));
+
+ u.addValue(new double[] { 1.0 });
+ assertFalse(Double.isNaN(u.getMean()[0]));
+ assertFalse(Double.isNaN(u.getMin()[0]));
+ assertFalse(Double.isNaN(u.getStandardDeviation()[0]));
+ assertFalse(Double.isNaN(u.getGeometricMean()[0]));
+
+ }
+
+ public void testSerialization() throws DimensionMismatchException {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(2, true);
+ // Empty test
+ TestUtils.checkSerializedEquality(u);
+ MultivariateSummaryStatistics s = (MultivariateSummaryStatistics) TestUtils.serializeAndRecover(u);
+ assertEquals(u, s);
+
+ // Add some data
+ u.addValue(new double[] { 2d, 1d });
+ u.addValue(new double[] { 1d, 1d });
+ u.addValue(new double[] { 3d, 1d });
+ u.addValue(new double[] { 4d, 1d });
+ u.addValue(new double[] { 5d, 1d });
+
+ // Test again
+ TestUtils.checkSerializedEquality(u);
+ s = (MultivariateSummaryStatistics) TestUtils.serializeAndRecover(u);
+ assertEquals(u, s);
+
+ }
+
+ public void testEqualsAndHashCode() throws DimensionMismatchException {
+ MultivariateSummaryStatistics u = createMultivariateSummaryStatistics(2, true);
+ MultivariateSummaryStatistics t = null;
+ int emptyHash = u.hashCode();
+ assertTrue(u.equals(u));
+ assertFalse(u.equals(t));
+ assertFalse(u.equals(Double.valueOf(0)));
+ t = createMultivariateSummaryStatistics(2, true);
+ assertTrue(t.equals(u));
+ assertTrue(u.equals(t));
+ assertEquals(emptyHash, t.hashCode());
+
+ // Add some data to u
+ u.addValue(new double[] { 2d, 1d });
+ u.addValue(new double[] { 1d, 1d });
+ u.addValue(new double[] { 3d, 1d });
+ u.addValue(new double[] { 4d, 1d });
+ u.addValue(new double[] { 5d, 1d });
+ assertFalse(t.equals(u));
+ assertFalse(u.equals(t));
+ assertTrue(u.hashCode() != t.hashCode());
+
+ //Add data in same order to t
+ t.addValue(new double[] { 2d, 1d });
+ t.addValue(new double[] { 1d, 1d });
+ t.addValue(new double[] { 3d, 1d });
+ t.addValue(new double[] { 4d, 1d });
+ t.addValue(new double[] { 5d, 1d });
+ assertTrue(t.equals(u));
+ assertTrue(u.equals(t));
+ assertEquals(u.hashCode(), t.hashCode());
+
+ // Clear and make sure summaries are indistinguishable from empty summary
+ u.clear();
+ t.clear();
+ assertTrue(t.equals(u));
+ assertTrue(u.equals(t));
+ assertEquals(emptyHash, t.hashCode());
+ assertEquals(emptyHash, u.hashCode());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/StatisticalSummaryValuesTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/StatisticalSummaryValuesTest.java
new file mode 100644
index 0000000..33ab5ed
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/StatisticalSummaryValuesTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+/**
+ * Test cases for the {@link StatisticalSummaryValues} class.
+ *
+ * @version $Revision: 1038873 $ $Date: 2010-11-25 00:35:13 +0100 (jeu. 25 nov. 2010) $
+ */
+
+public final class StatisticalSummaryValuesTest extends TestCase {
+
+
+ public StatisticalSummaryValuesTest(String name) {
+ super(name);
+ }
+
+ public void testSerialization() {
+ StatisticalSummaryValues u = new StatisticalSummaryValues(1, 2, 3, 4, 5, 6);
+ TestUtils.checkSerializedEquality(u);
+ StatisticalSummaryValues t = (StatisticalSummaryValues) TestUtils.serializeAndRecover(u);
+ verifyEquality(u, t);
+ }
+
+ public void testEqualsAndHashCode() {
+ StatisticalSummaryValues u = new StatisticalSummaryValues(1, 2, 3, 4, 5, 6);
+ StatisticalSummaryValues t = null;
+ assertTrue("reflexive", u.equals(u));
+ assertFalse("non-null compared to null", u.equals(t));
+ assertFalse("wrong type", u.equals(Double.valueOf(0)));
+ t = new StatisticalSummaryValues(1, 2, 3, 4, 5, 6);
+ assertTrue("instances with same data should be equal", t.equals(u));
+ assertEquals("hash code", u.hashCode(), t.hashCode());
+
+ u = new StatisticalSummaryValues(Double.NaN, 2, 3, 4, 5, 6);
+ t = new StatisticalSummaryValues(1, Double.NaN, 3, 4, 5, 6);
+ assertFalse("instances based on different data should be different",
+ (u.equals(t) ||t.equals(u)));
+ }
+
+ private void verifyEquality(StatisticalSummaryValues s, StatisticalSummaryValues u) {
+ assertEquals("N",s.getN(),u.getN());
+ TestUtils.assertEquals("sum",s.getSum(),u.getSum(), 0);
+ TestUtils.assertEquals("var",s.getVariance(),u.getVariance(), 0);
+ TestUtils.assertEquals("std",s.getStandardDeviation(),u.getStandardDeviation(), 0);
+ TestUtils.assertEquals("mean",s.getMean(),u.getMean(), 0);
+ TestUtils.assertEquals("min",s.getMin(),u.getMin(), 0);
+ TestUtils.assertEquals("max",s.getMax(),u.getMax(), 0);
+ }
+
+ public void testToString() {
+ StatisticalSummaryValues u = new StatisticalSummaryValues(4.5, 16, 10, 5, 4, 45);
+ Locale d = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ assertEquals("StatisticalSummaryValues:\n" +
+ "n: 10\n" +
+ "min: 4.0\n" +
+ "max: 5.0\n" +
+ "mean: 4.5\n" +
+ "std dev: 4.0\n" +
+ "variance: 16.0\n" +
+ "sum: 45.0\n", u.toString());
+ Locale.setDefault(d);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatisticAbstractTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatisticAbstractTest.java
new file mode 100644
index 0000000..6e97b3f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/StorelessUnivariateStatisticAbstractTest.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.stat.descriptive;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.moment.SecondMoment;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for {@link StorelessUnivariateStatistic} classes.
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public abstract class StorelessUnivariateStatisticAbstractTest
+ extends UnivariateStatisticAbstractTest {
+
+ public StorelessUnivariateStatisticAbstractTest(String name) {
+ super(name);
+ }
+
+ /** Small sample arrays */
+ protected double[][] smallSamples = {{}, {1}, {1,2}, {1,2,3}, {1,2,3,4}};
+
+ /** Return a new instance of the statistic */
+ @Override
+ public abstract UnivariateStatistic getUnivariateStatistic();
+
+ /**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
+ @Override
+ public abstract double expectedValue();
+
+ /**
+ * Verifies that increment() and incrementAll work properly.
+ */
+ public void testIncrementation() throws Exception {
+
+ StorelessUnivariateStatistic statistic =
+ (StorelessUnivariateStatistic) getUnivariateStatistic();
+
+ // Add testArray one value at a time and check result
+ for (int i = 0; i < testArray.length; i++) {
+ statistic.increment(testArray[i]);
+ }
+
+ assertEquals(expectedValue(), statistic.getResult(), getTolerance());
+ assertEquals(testArray.length, statistic.getN());
+
+ statistic.clear();
+
+ // Add testArray all at once and check again
+ statistic.incrementAll(testArray);
+ assertEquals(expectedValue(), statistic.getResult(), getTolerance());
+ assertEquals(testArray.length, statistic.getN());
+
+ statistic.clear();
+
+ // Cleared
+ assertTrue(Double.isNaN(statistic.getResult()));
+ assertEquals(0, statistic.getN());
+
+ }
+
+ public void testSerialization() throws Exception {
+
+ StorelessUnivariateStatistic statistic =
+ (StorelessUnivariateStatistic) getUnivariateStatistic();
+
+ TestUtils.checkSerializedEquality(statistic);
+
+ statistic.clear();
+
+ for (int i = 0; i < testArray.length; i++) {
+ statistic.increment(testArray[i]);
+ if(i % 5 == 0)
+ statistic = (StorelessUnivariateStatistic)TestUtils.serializeAndRecover(statistic);
+ }
+
+ TestUtils.checkSerializedEquality(statistic);
+
+ assertEquals(expectedValue(), statistic.getResult(), getTolerance());
+
+ statistic.clear();
+
+ assertTrue(Double.isNaN(statistic.getResult()));
+
+ }
+
+ public void testEqualsAndHashCode() {
+ StorelessUnivariateStatistic statistic =
+ (StorelessUnivariateStatistic) getUnivariateStatistic();
+ StorelessUnivariateStatistic statistic2 = null;
+
+ assertTrue("non-null, compared to null", !statistic.equals(statistic2));
+ assertTrue("reflexive, non-null", statistic.equals(statistic));
+
+ int emptyHash = statistic.hashCode();
+ statistic2 = (StorelessUnivariateStatistic) getUnivariateStatistic();
+ assertTrue("empty stats should be equal", statistic.equals(statistic2));
+ assertEquals("empty stats should have the same hashcode",
+ emptyHash, statistic2.hashCode());
+
+ statistic.increment(1d);
+ assertTrue("reflexive, non-empty", statistic.equals(statistic));
+ assertTrue("non-empty, compared to empty", !statistic.equals(statistic2));
+ assertTrue("non-empty, compared to empty", !statistic2.equals(statistic));
+ assertTrue("non-empty stat should have different hashcode from empty stat",
+ statistic.hashCode() != emptyHash);
+
+ statistic2.increment(1d);
+ assertTrue("stats with same data should be equal", statistic.equals(statistic2));
+ assertEquals("stats with same data should have the same hashcode",
+ statistic.hashCode(), statistic2.hashCode());
+
+ statistic.increment(Double.POSITIVE_INFINITY);
+ assertTrue("stats with different n's should not be equal", !statistic2.equals(statistic));
+ assertTrue("stats with different n's should have different hashcodes",
+ statistic.hashCode() != statistic2.hashCode());
+
+ statistic2.increment(Double.POSITIVE_INFINITY);
+ assertTrue("stats with same data should be equal", statistic.equals(statistic2));
+ assertEquals("stats with same data should have the same hashcode",
+ statistic.hashCode(), statistic2.hashCode());
+
+ statistic.clear();
+ statistic2.clear();
+ assertTrue("cleared stats should be equal", statistic.equals(statistic2));
+ assertEquals("cleared stats should have thashcode of empty stat",
+ emptyHash, statistic2.hashCode());
+ assertEquals("cleared stats should have thashcode of empty stat",
+ emptyHash, statistic.hashCode());
+
+ }
+
+ public void testMomentSmallSamples() {
+ UnivariateStatistic stat = getUnivariateStatistic();
+ if (stat instanceof SecondMoment) {
+ SecondMoment moment = (SecondMoment) getUnivariateStatistic();
+ assertTrue(Double.isNaN(moment.getResult()));
+ moment.increment(1d);
+ assertEquals(0d, moment.getResult(), 0);
+ }
+ }
+
+ /**
+ * Make sure that evaluate(double[]) and inrementAll(double[]),
+ * getResult() give same results.
+ */
+ public void testConsistency() {
+ StorelessUnivariateStatistic stat = (StorelessUnivariateStatistic) getUnivariateStatistic();
+ stat.incrementAll(testArray);
+ assertEquals(stat.getResult(), stat.evaluate(testArray), getTolerance());
+ for (int i = 0; i < smallSamples.length; i++) {
+ stat.clear();
+ for (int j =0; j < smallSamples[i].length; j++) {
+ stat.increment(smallSamples[i][j]);
+ }
+ TestUtils.assertEquals(stat.getResult(), stat.evaluate(smallSamples[i]), getTolerance());
+ }
+ }
+
+ /**
+ * Verifies that copied statistics remain equal to originals when
+ * incremented the same way.
+ *
+ */
+ public void testCopyConsistency() {
+
+ StorelessUnivariateStatistic master =
+ (StorelessUnivariateStatistic) getUnivariateStatistic();
+
+ StorelessUnivariateStatistic replica = null;
+
+ // Randomly select a portion of testArray to load first
+ long index = FastMath.round((FastMath.random()) * testArray.length);
+
+ // Put first half in master and copy master to replica
+ master.incrementAll(testArray, 0, (int) index);
+ replica = master.copy();
+
+ // Check same
+ assertTrue(replica.equals(master));
+ assertTrue(master.equals(replica));
+
+ // Now add second part to both and check again
+ master.incrementAll(testArray,
+ (int) index, (int) (testArray.length - index));
+ replica.incrementAll(testArray,
+ (int) index, (int) (testArray.length - index));
+ assertTrue(replica.equals(master));
+ assertTrue(master.equals(replica));
+ }
+
+ public void testSerial() {
+ StorelessUnivariateStatistic s =
+ (StorelessUnivariateStatistic) getUnivariateStatistic();
+ assertEquals(s, TestUtils.serializeAndRecover(s));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsTest.java
new file mode 100644
index 0000000..3dfac2f
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/SummaryStatisticsTest.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 junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.summary.Sum;
+import org.apache.commons.math.util.FastMath;
+/**
+ * Test cases for the {@link SummaryStatistics} class.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+
+public class SummaryStatisticsTest extends TestCase {
+
+ private double one = 1;
+ private float twoF = 2;
+ private long twoL = 2;
+ private int three = 3;
+ private double mean = 2;
+ private double sumSq = 18;
+ private double sum = 8;
+ private double var = 0.666666666666666666667;
+ private double std = FastMath.sqrt(var);
+ private double n = 4;
+ private double min = 1;
+ private double max = 3;
+ private double tolerance = 10E-15;
+
+ public SummaryStatisticsTest(String name) {
+ super(name);
+ }
+
+ protected SummaryStatistics createSummaryStatistics() {
+ return new SummaryStatistics();
+ }
+
+ /** test stats */
+ public void testStats() {
+ SummaryStatistics u = createSummaryStatistics();
+ assertEquals("total count",0,u.getN(),tolerance);
+ u.addValue(one);
+ u.addValue(twoF);
+ u.addValue(twoL);
+ u.addValue(three);
+ assertEquals("N",n,u.getN(),tolerance);
+ assertEquals("sum",sum,u.getSum(),tolerance);
+ assertEquals("sumsq",sumSq,u.getSumsq(),tolerance);
+ assertEquals("var",var,u.getVariance(),tolerance);
+ assertEquals("std",std,u.getStandardDeviation(),tolerance);
+ assertEquals("mean",mean,u.getMean(),tolerance);
+ assertEquals("min",min,u.getMin(),tolerance);
+ assertEquals("max",max,u.getMax(),tolerance);
+ u.clear();
+ assertEquals("total count",0,u.getN(),tolerance);
+ }
+
+ public void testN0andN1Conditions() throws Exception {
+ SummaryStatistics u = createSummaryStatistics();
+ assertTrue("Mean of n = 0 set should be NaN",
+ Double.isNaN( u.getMean() ) );
+ assertTrue("Standard Deviation of n = 0 set should be NaN",
+ Double.isNaN( u.getStandardDeviation() ) );
+ assertTrue("Variance of n = 0 set should be NaN",
+ Double.isNaN(u.getVariance() ) );
+
+ /* n=1 */
+ u.addValue(one);
+ assertTrue("mean should be one (n = 1)",
+ u.getMean() == one);
+ assertTrue("geometric should be one (n = 1) instead it is " + u.getGeometricMean(),
+ u.getGeometricMean() == one);
+ assertTrue("Std should be zero (n = 1)",
+ u.getStandardDeviation() == 0.0);
+ assertTrue("variance should be zero (n = 1)",
+ u.getVariance() == 0.0);
+
+ /* n=2 */
+ u.addValue(twoF);
+ assertTrue("Std should not be zero (n = 2)",
+ u.getStandardDeviation() != 0.0);
+ assertTrue("variance should not be zero (n = 2)",
+ u.getVariance() != 0.0);
+
+ }
+
+ public void testProductAndGeometricMean() throws Exception {
+ SummaryStatistics u = createSummaryStatistics();
+ u.addValue( 1.0 );
+ u.addValue( 2.0 );
+ u.addValue( 3.0 );
+ u.addValue( 4.0 );
+
+ assertEquals( "Geometric mean not expected", 2.213364,
+ u.getGeometricMean(), 0.00001 );
+ }
+
+ public void testNaNContracts() {
+ SummaryStatistics u = createSummaryStatistics();
+ assertTrue("mean not NaN",Double.isNaN(u.getMean()));
+ assertTrue("min not NaN",Double.isNaN(u.getMin()));
+ assertTrue("std dev not NaN",Double.isNaN(u.getStandardDeviation()));
+ assertTrue("var not NaN",Double.isNaN(u.getVariance()));
+ assertTrue("geom mean not NaN",Double.isNaN(u.getGeometricMean()));
+
+ u.addValue(1.0);
+
+ assertEquals( "mean not expected", 1.0,
+ u.getMean(), Double.MIN_VALUE);
+ assertEquals( "variance not expected", 0.0,
+ u.getVariance(), Double.MIN_VALUE);
+ assertEquals( "geometric mean not expected", 1.0,
+ u.getGeometricMean(), Double.MIN_VALUE);
+
+ u.addValue(-1.0);
+
+ assertTrue("geom mean not NaN",Double.isNaN(u.getGeometricMean()));
+
+ u.addValue(0.0);
+
+ assertTrue("geom mean not NaN",Double.isNaN(u.getGeometricMean()));
+
+ //FiXME: test all other NaN contract specs
+ }
+
+ public void testGetSummary() {
+ SummaryStatistics u = createSummaryStatistics();
+ StatisticalSummary summary = u.getSummary();
+ verifySummary(u, summary);
+ u.addValue(1d);
+ summary = u.getSummary();
+ verifySummary(u, summary);
+ u.addValue(2d);
+ summary = u.getSummary();
+ verifySummary(u, summary);
+ u.addValue(2d);
+ summary = u.getSummary();
+ verifySummary(u, summary);
+ }
+
+ public void testSerialization() {
+ SummaryStatistics u = createSummaryStatistics();
+ // Empty test
+ TestUtils.checkSerializedEquality(u);
+ SummaryStatistics s = (SummaryStatistics) TestUtils.serializeAndRecover(u);
+ StatisticalSummary summary = s.getSummary();
+ verifySummary(u, summary);
+
+ // Add some data
+ u.addValue(2d);
+ u.addValue(1d);
+ u.addValue(3d);
+ u.addValue(4d);
+ u.addValue(5d);
+
+ // Test again
+ TestUtils.checkSerializedEquality(u);
+ s = (SummaryStatistics) TestUtils.serializeAndRecover(u);
+ summary = s.getSummary();
+ verifySummary(u, summary);
+
+ }
+
+ public void testEqualsAndHashCode() {
+ SummaryStatistics u = createSummaryStatistics();
+ SummaryStatistics t = null;
+ int emptyHash = u.hashCode();
+ assertTrue("reflexive", u.equals(u));
+ assertFalse("non-null compared to null", u.equals(t));
+ assertFalse("wrong type", u.equals(Double.valueOf(0)));
+ t = createSummaryStatistics();
+ assertTrue("empty instances should be equal", t.equals(u));
+ assertTrue("empty instances should be equal", u.equals(t));
+ assertEquals("empty hash code", emptyHash, t.hashCode());
+
+ // Add some data to u
+ u.addValue(2d);
+ u.addValue(1d);
+ u.addValue(3d);
+ u.addValue(4d);
+ assertFalse("different n's should make instances not equal", t.equals(u));
+ assertFalse("different n's should make instances not equal", u.equals(t));
+ assertTrue("different n's should make hashcodes different",
+ u.hashCode() != t.hashCode());
+
+ //Add data in same order to t
+ t.addValue(2d);
+ t.addValue(1d);
+ t.addValue(3d);
+ t.addValue(4d);
+ assertTrue("summaries based on same data should be equal", t.equals(u));
+ assertTrue("summaries based on same data should be equal", u.equals(t));
+ assertEquals("summaries based on same data should have same hashcodes",
+ u.hashCode(), t.hashCode());
+
+ // Clear and make sure summaries are indistinguishable from empty summary
+ u.clear();
+ t.clear();
+ assertTrue("empty instances should be equal", t.equals(u));
+ assertTrue("empty instances should be equal", u.equals(t));
+ assertEquals("empty hash code", emptyHash, t.hashCode());
+ assertEquals("empty hash code", emptyHash, u.hashCode());
+ }
+
+ public void testCopy() throws Exception {
+ SummaryStatistics u = createSummaryStatistics();
+ u.addValue(2d);
+ u.addValue(1d);
+ u.addValue(3d);
+ u.addValue(4d);
+ SummaryStatistics v = new SummaryStatistics(u);
+ assertEquals(u, v);
+ assertEquals(v, u);
+ assertTrue(v.geoMean == v.getGeoMeanImpl());
+ assertTrue(v.mean == v.getMeanImpl());
+ assertTrue(v.min == v.getMinImpl());
+ assertTrue(v.max == v.getMaxImpl());
+ assertTrue(v.sum == v.getSumImpl());
+ assertTrue(v.sumsq == v.getSumsqImpl());
+ assertTrue(v.sumLog == v.getSumLogImpl());
+ assertTrue(v.variance == v.getVarianceImpl());
+
+ // Make sure both behave the same with additional values added
+ u.addValue(7d);
+ u.addValue(9d);
+ u.addValue(11d);
+ u.addValue(23d);
+ v.addValue(7d);
+ v.addValue(9d);
+ v.addValue(11d);
+ v.addValue(23d);
+ assertEquals(u, v);
+ assertEquals(v, u);
+
+ // Check implementation pointers are preserved
+ u.clear();
+ u.setSumImpl(new Sum());
+ SummaryStatistics.copy(u,v);
+ assertEquals(u.sum, v.sum);
+ assertEquals(u.getSumImpl(), v.getSumImpl());
+
+ }
+
+ private void verifySummary(SummaryStatistics u, StatisticalSummary s) {
+ assertEquals("N",s.getN(),u.getN());
+ TestUtils.assertEquals("sum",s.getSum(),u.getSum(),tolerance);
+ TestUtils.assertEquals("var",s.getVariance(),u.getVariance(),tolerance);
+ TestUtils.assertEquals("std",s.getStandardDeviation(),u.getStandardDeviation(),tolerance);
+ TestUtils.assertEquals("mean",s.getMean(),u.getMean(),tolerance);
+ TestUtils.assertEquals("min",s.getMin(),u.getMin(),tolerance);
+ TestUtils.assertEquals("max",s.getMax(),u.getMax(),tolerance);
+ }
+
+ public void testSetterInjection() throws Exception {
+ SummaryStatistics u = createSummaryStatistics();
+ u.setMeanImpl(new Sum());
+ u.setSumLogImpl(new Sum());
+ u.addValue(1);
+ u.addValue(3);
+ assertEquals(4, u.getMean(), 1E-14);
+ assertEquals(4, u.getSumOfLogs(), 1E-14);
+ assertEquals(FastMath.exp(2), u.getGeometricMean(), 1E-14);
+ u.clear();
+ u.addValue(1);
+ u.addValue(2);
+ assertEquals(3, u.getMean(), 1E-14);
+ u.clear();
+ u.setMeanImpl(new Mean()); // OK after clear
+ }
+
+ public void testSetterIllegalState() throws Exception {
+ SummaryStatistics u = createSummaryStatistics();
+ u.addValue(1);
+ u.addValue(3);
+ try {
+ u.setMeanImpl(new Sum());
+ fail("Expecting IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatisticsTest.java
new file mode 100644
index 0000000..ac51256
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedDescriptiveStatisticsTest.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.stat.descriptive;
+
+
+/**
+ * Test cases for the {@link SynchronizedDescriptiveStatisticsTest} class.
+ * @version $Revision: 902201 $ $Date: 2007-08-16 15:36:33 -0500 (Thu, 16 Aug
+ * 2007) $
+ */
+public final class SynchronizedDescriptiveStatisticsTest extends DescriptiveStatisticsTest {
+
+ public SynchronizedDescriptiveStatisticsTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected DescriptiveStatistics createDescriptiveStatistics() {
+ return new SynchronizedDescriptiveStatistics();
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatisticsTest.java
new file mode 100644
index 0000000..1282f8d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedMultivariateSummaryStatisticsTest.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.stat.descriptive;
+
+
+/**
+ * Test cases for the {@link SynchronizedMultivariateSummaryStatisticsTest} class.
+ * @version $Revision: 902201 $ $Date: 2007-08-16 15:36:33 -0500 (Thu, 16 Aug
+ * 2007) $
+ */
+public final class SynchronizedMultivariateSummaryStatisticsTest extends MultivariateSummaryStatisticsTest {
+
+ public SynchronizedMultivariateSummaryStatisticsTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected MultivariateSummaryStatistics createMultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
+ return new SynchronizedMultivariateSummaryStatistics(k, isCovarianceBiasCorrected);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatisticsTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatisticsTest.java
new file mode 100644
index 0000000..2418a87
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/SynchronizedSummaryStatisticsTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
+ * or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive;
+
+/**
+ * Test cases for the {@link SynchronizedSummaryStatisticsTest} class.
+ * @version $Revision: 902201 $ $Date: 2007-08-16 15:36:33 -0500 (Thu, 16 Aug
+ * 2007) $
+ */
+public final class SynchronizedSummaryStatisticsTest extends SummaryStatisticsTest {
+
+ public SynchronizedSummaryStatisticsTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected SummaryStatistics createSummaryStatistics() {
+ return new SynchronizedSummaryStatistics();
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/UnivariateStatisticAbstractTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/UnivariateStatisticAbstractTest.java
new file mode 100644
index 0000000..a01503a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/UnivariateStatisticAbstractTest.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
+ *
+s * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.random.RandomData;
+import org.apache.commons.math.random.RandomDataImpl;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public abstract class UnivariateStatisticAbstractTest extends TestCase {
+
+ protected double mean = 12.404545454545455d;
+ protected double geoMean = 12.070589161633011d;
+
+ protected double var = 10.00235930735931d;
+ protected double std = FastMath.sqrt(var);
+ protected double skew = 1.437423729196190d;
+ protected double kurt = 2.377191264804700d;
+
+ protected double min = 8.2d;
+ protected double max = 21d;
+ protected double median = 12d;
+ protected double percentile5 = 8.29d;
+ protected double percentile95 = 20.82d;
+
+ protected double product = 628096400563833396009676.9200400128d;
+ protected double sumLog = 54.7969806116451507d;
+ protected double sumSq = 3595.250d;
+ protected double sum = 272.90d;
+ protected double secondMoment = 210.04954545454547d;
+ protected double thirdMoment = 868.0906859504136;
+ protected double fourthMoment = 9244.080993773481;
+
+
+ protected double weightedMean = 12.366995073891626d;
+ protected double weightedVar = 9.974760968886391d;
+ protected double weightedStd = FastMath.sqrt(weightedVar);
+ protected double weightedProduct = 8517647448765288000000d;
+ protected double weightedSum = 251.05d;
+
+ protected double tolerance = 10E-12;
+
+ protected double[] testArray =
+ { 12.5, 12.0, 11.8, 14.2, 14.9, 14.5, 21.0, 8.2, 10.3, 11.3,
+ 14.1, 9.9, 12.2, 12.0, 12.1, 11.0, 19.8, 11.0, 10.0, 8.8,
+ 9.0, 12.3 };
+
+ protected double[] testWeightsArray =
+ { 1.5, 0.8, 1.2, 0.4, 0.8, 1.8, 1.2, 1.1, 1.0, 0.7,
+ 1.3, 0.6, 0.7, 1.3, 0.7, 1.0, 0.4, 0.1, 1.4, 0.9,
+ 1.1, 0.3 };
+
+ protected double[] identicalWeightsArray =
+ { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5 };
+
+ protected double[] unitWeightsArray =
+ { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
+ 1.0, 1.0 };
+
+
+ public UnivariateStatisticAbstractTest(String name) {
+ super(name);
+ }
+
+ public abstract UnivariateStatistic getUnivariateStatistic();
+
+ public abstract double expectedValue();
+
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ public void testEvaluation() throws Exception {
+ assertEquals(
+ expectedValue(),
+ getUnivariateStatistic().evaluate(testArray),
+ getTolerance());
+ }
+
+ public void testCopy() throws Exception {
+ UnivariateStatistic original = getUnivariateStatistic();
+ UnivariateStatistic copy = original.copy();
+ assertEquals(
+ expectedValue(),
+ copy.evaluate(testArray),
+ getTolerance());
+ }
+
+ /**
+ * Tests consistency of weighted statistic computation.
+ * For statistics that support weighted evaluation, this test case compares
+ * the result of direct computation on an array with repeated values with
+ * a weighted computation on the corresponding (shorter) array with each
+ * value appearing only once but with a weight value equal to its multiplicity
+ * in the repeating array.
+ */
+
+ public void testWeightedConsistency() throws Exception {
+
+ // See if this statistic computes weighted statistics
+ // If not, skip this test
+ UnivariateStatistic statistic = getUnivariateStatistic();
+ if (!(statistic instanceof WeightedEvaluation)) {
+ return;
+ }
+
+ // Create arrays of values and corresponding integral weights
+ // and longer array with values repeated according to the weights
+ final int len = 10; // length of values array
+ final double mu = 0; // mean of test data
+ final double sigma = 5; // std dev of test data
+ double[] values = new double[len];
+ double[] weights = new double[len];
+ RandomData randomData = new RandomDataImpl();
+
+ // Fill weights array with random int values between 1 and 5
+ int[] intWeights = new int[len];
+ for (int i = 0; i < len; i++) {
+ intWeights[i] = randomData.nextInt(1, 5);
+ weights[i] = intWeights[i];
+ }
+
+ // Fill values array with random data from N(mu, sigma)
+ // and fill valuesList with values from values array with
+ // values[i] repeated weights[i] times, each i
+ List<Double> valuesList = new ArrayList<Double>();
+ for (int i = 0; i < len; i++) {
+ double value = randomData.nextGaussian(mu, sigma);
+ values[i] = value;
+ for (int j = 0; j < intWeights[i]; j++) {
+ valuesList.add(new Double(value));
+ }
+ }
+
+ // Dump valuesList into repeatedValues array
+ int sumWeights = valuesList.size();
+ double[] repeatedValues = new double[sumWeights];
+ for (int i = 0; i < sumWeights; i++) {
+ repeatedValues[i] = valuesList.get(i);
+ }
+
+ // Compare result of weighted statistic computation with direct computation
+ // on array of repeated values
+ WeightedEvaluation weightedStatistic = (WeightedEvaluation) statistic;
+ TestUtils.assertRelativelyEquals(statistic.evaluate(repeatedValues),
+ weightedStatistic.evaluate(values, weights, 0, values.length),
+ 10E-14);
+
+ // Check consistency of weighted evaluation methods
+ assertEquals(weightedStatistic.evaluate(values, weights, 0, values.length),
+ weightedStatistic.evaluate(values, weights), Double.MIN_VALUE);
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/FirstMomentTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/FirstMomentTest.java
new file mode 100644
index 0000000..fb13927
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/FirstMomentTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link FirstMoment} class.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class FirstMomentTest extends StorelessUnivariateStatisticAbstractTest{
+
+ /** descriptive statistic. */
+ protected FirstMoment stat;
+
+ /**
+ * @param name
+ */
+ public FirstMomentTest(String name) {
+ super(name);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#getUnivariateStatistic()
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new FirstMoment();
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#expectedValue()
+ */
+ @Override
+ public double expectedValue() {
+ return this.mean;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/FourthMomentTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/FourthMomentTest.java
new file mode 100644
index 0000000..5219d10
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/FourthMomentTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link FourthMoment} class.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class FourthMomentTest extends StorelessUnivariateStatisticAbstractTest{
+
+ /** descriptive statistic. */
+ protected FourthMoment stat;
+
+ /**
+ * @param name
+ */
+ public FourthMomentTest(String name) {
+ super(name);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#getUnivariateStatistic()
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new FourthMoment();
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#expectedValue()
+ */
+ @Override
+ public double expectedValue() {
+ return this.fourthMoment;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/GeometricMeanTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/GeometricMeanTest.java
new file mode 100644
index 0000000..e021a42
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/GeometricMeanTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class GeometricMeanTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected GeometricMean stat;
+
+ /**
+ * @param name
+ */
+ public GeometricMeanTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new GeometricMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.geoMean;
+ }
+
+ public void testSpecialValues() {
+ GeometricMean mean = new GeometricMean();
+ // empty
+ assertTrue(Double.isNaN(mean.getResult()));
+
+ // finite data
+ mean.increment(1d);
+ assertFalse(Double.isNaN(mean.getResult()));
+
+ // add 0 -- makes log sum blow to minus infinity, should make 0
+ mean.increment(0d);
+ assertEquals(0d, mean.getResult(), 0);
+
+ // add positive infinity - note the minus infinity above
+ mean.increment(Double.POSITIVE_INFINITY);
+ assertTrue(Double.isNaN(mean.getResult()));
+
+ // clear
+ mean.clear();
+ assertTrue(Double.isNaN(mean.getResult()));
+
+ // positive infinity by itself
+ mean.increment(Double.POSITIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, mean.getResult(), 0);
+
+ // negative value -- should make NaN
+ mean.increment(-2d);
+ assertTrue(Double.isNaN(mean.getResult()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/KurtosisTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/KurtosisTest.java
new file mode 100644
index 0000000..62878cd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/KurtosisTest.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.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class KurtosisTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Kurtosis stat;
+
+ /**
+ * @param name
+ */
+ public KurtosisTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Kurtosis();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.kurt;
+ }
+
+ /**
+ * Make sure Double.NaN is returned iff n < 4
+ *
+ */
+ public void testNaN() {
+ Kurtosis kurt = new Kurtosis();
+ assertTrue(Double.isNaN(kurt.getResult()));
+ kurt.increment(1d);
+ assertTrue(Double.isNaN(kurt.getResult()));
+ kurt.increment(1d);
+ assertTrue(Double.isNaN(kurt.getResult()));
+ kurt.increment(1d);
+ assertTrue(Double.isNaN(kurt.getResult()));
+ kurt.increment(1d);
+ assertFalse(Double.isNaN(kurt.getResult()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/MeanTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/MeanTest.java
new file mode 100644
index 0000000..102803c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/MeanTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class MeanTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Mean stat;
+
+ /**
+ * @param name
+ */
+ public MeanTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Mean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.mean;
+ }
+
+ /**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
+ public double expectedWeightedValue() {
+ return this.weightedMean;
+ }
+
+ public void testSmallSamples() {
+ Mean mean = new Mean();
+ assertTrue(Double.isNaN(mean.getResult()));
+ mean.increment(1d);
+ assertEquals(1d, mean.getResult(), 0);
+ }
+
+ public void testWeightedMean() {
+ Mean mean = new Mean();
+ assertEquals(expectedWeightedValue(), mean.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
+ assertEquals(expectedValue(), mean.evaluate(testArray, identicalWeightsArray, 0, testArray.length), getTolerance());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/SecondMomentTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SecondMomentTest.java
new file mode 100644
index 0000000..7af7774
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SecondMomentTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link SecondMoment} class.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class SecondMomentTest extends StorelessUnivariateStatisticAbstractTest {
+
+ /** descriptive statistic. */
+ protected SecondMoment stat;
+
+ /**
+ * @param name
+ */
+ public SecondMomentTest(String name) {
+ super(name);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#getUnivariateStatistic()
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new SecondMoment();
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#expectedValue()
+ */
+ @Override
+ public double expectedValue() {
+ return this.secondMoment;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/SemiVarianceTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SemiVarianceTest.java
new file mode 100644
index 0000000..24b1512
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SemiVarianceTest.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 org.apache.commons.math.TestUtils;
+import org.apache.commons.math.stat.StatUtils;
+
+import junit.framework.TestCase;
+
+public class SemiVarianceTest extends TestCase {
+
+ public void testInsufficientData() {
+ double[] nothing = null;
+ SemiVariance sv = new SemiVariance();
+ try {
+ sv.evaluate(nothing);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException iae) {
+ }
+
+ try {
+ sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
+ sv.evaluate(nothing);
+ fail("null is not a valid data array.");
+ } catch (IllegalArgumentException iae) {
+ }
+ nothing = new double[] {};
+ assertTrue(Double.isNaN(sv.evaluate(nothing)));
+ }
+
+ public void testSingleDown() {
+ SemiVariance sv = new SemiVariance();
+ double[] values = { 50.0d };
+ double singletest = sv.evaluate(values);
+ assertEquals(0.0d, singletest, 0);
+ }
+
+ public void testSingleUp() {
+ SemiVariance sv = new SemiVariance(SemiVariance.UPSIDE_VARIANCE);
+ double[] values = { 50.0d };
+ double singletest = sv.evaluate(values);
+ assertEquals(0.0d, singletest, 0);
+ }
+
+ public void testSample() {
+ final double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
+ final int length = values.length;
+ final double mean = StatUtils.mean(values); // 6.333...
+ final SemiVariance sv = new SemiVariance(); // Default bias correction is true
+ final double downsideSemiVariance = sv.evaluate(values); // Downside is the default
+ assertEquals(TestUtils.sumSquareDev(new double[] {-2d, 2d, 4d, -2d, 3d, 5d}, mean) / (length - 1),
+ downsideSemiVariance, 1E-14);
+
+ sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
+ final double upsideSemiVariance = sv.evaluate(values);
+ assertEquals(TestUtils.sumSquareDev(new double[] {22d, 11d, 14d}, mean) / (length - 1),
+ upsideSemiVariance, 1E-14);
+
+ // Verify that upper + lower semivariance against the mean sum to variance
+ assertEquals(StatUtils.variance(values), downsideSemiVariance + upsideSemiVariance, 10e-12);
+ }
+
+ public void testPopulation() {
+ double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
+ SemiVariance sv = new SemiVariance(false);
+
+ double singletest = sv.evaluate(values);
+ assertEquals(19.556d, singletest, 0.01d);
+
+ sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
+ singletest = sv.evaluate(values);
+ assertEquals(36.222d, singletest, 0.01d);
+ }
+
+ public void testNonMeanCutoffs() {
+ double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
+ SemiVariance sv = new SemiVariance(false); // Turn off bias correction - use df = length
+
+ double singletest = sv.evaluate(values, 1.0d, SemiVariance.DOWNSIDE_VARIANCE, false, 0, values.length);
+ assertEquals(TestUtils.sumSquareDev(new double[] { -2d, -2d }, 1.0d) / values.length,
+ singletest, 0.01d);
+
+ singletest = sv.evaluate(values, 3.0d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length);
+ assertEquals(TestUtils.sumSquareDev(new double[] { 4d, 22d, 11d, 14d, 5d }, 3.0d) / values.length, singletest,
+ 0.01d);
+ }
+
+ /**
+ * Check that the lower + upper semivariance against the mean sum to the
+ * variance.
+ */
+ public void testVarianceDecompMeanCutoff() {
+ double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
+ double variance = StatUtils.variance(values);
+ SemiVariance sv = new SemiVariance(true); // Bias corrected
+ sv.setVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE);
+ final double lower = sv.evaluate(values);
+ sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
+ final double upper = sv.evaluate(values);
+ assertEquals(variance, lower + upper, 10e-12);
+ }
+
+ /**
+ * Check that upper and lower semivariances against a cutoff sum to the sum
+ * of squared deviations of the full set of values against the cutoff
+ * divided by df = length - 1 (assuming bias-corrected).
+ */
+ public void testVarianceDecompNonMeanCutoff() {
+ double[] values = { -2.0d, 2.0d, 4.0d, -2.0d, 22.0d, 11.0d, 3.0d, 14.0d, 5.0d };
+ double target = 0;
+ double totalSumOfSquares = TestUtils.sumSquareDev(values, target);
+ SemiVariance sv = new SemiVariance(true); // Bias corrected
+ sv.setVarianceDirection(SemiVariance.DOWNSIDE_VARIANCE);
+ double lower = sv.evaluate(values, target);
+ sv.setVarianceDirection(SemiVariance.UPSIDE_VARIANCE);
+ double upper = sv.evaluate(values, target);
+ assertEquals(totalSumOfSquares / (values.length - 1), lower + upper, 10e-12);
+ }
+
+ public void testNoVariance() {
+ final double[] values = {100d, 100d, 100d, 100d};
+ SemiVariance sv = new SemiVariance();
+ assertEquals(0, sv.evaluate(values), 10E-12);
+ assertEquals(0, sv.evaluate(values, 100d), 10E-12);
+ assertEquals(0, sv.evaluate(values, 100d, SemiVariance.UPSIDE_VARIANCE, false, 0, values.length), 10E-12);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/SkewnessTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SkewnessTest.java
new file mode 100644
index 0000000..e0f4bd4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/SkewnessTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class SkewnessTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Skewness stat;
+
+ /**
+ * @param name
+ */
+ public SkewnessTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Skewness();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.skew;
+ }
+
+ /**
+ * Make sure Double.NaN is returned iff n < 3
+ *
+ */
+ public void testNaN() {
+ Skewness skew = new Skewness();
+ assertTrue(Double.isNaN(skew.getResult()));
+ skew.increment(1d);
+ assertTrue(Double.isNaN(skew.getResult()));
+ skew.increment(1d);
+ assertTrue(Double.isNaN(skew.getResult()));
+ skew.increment(1d);
+ assertFalse(Double.isNaN(skew.getResult()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviationTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviationTest.java
new file mode 100644
index 0000000..9429fcd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/StandardDeviationTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.util.FastMath;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ *
+ * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
+ */
+public class StandardDeviationTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected StandardDeviation stat;
+
+ /**
+ * @param name
+ */
+ public StandardDeviationTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new StandardDeviation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.std;
+ }
+
+ /**
+ * Make sure Double.NaN is returned iff n = 0
+ *
+ */
+ public void testNaN() {
+ StandardDeviation std = new StandardDeviation();
+ assertTrue(Double.isNaN(std.getResult()));
+ std.increment(1d);
+ assertEquals(0d, std.getResult(), 0);
+ }
+
+ /**
+ * Test population version of variance
+ */
+ public void testPopulation() {
+ double[] values = {-1.0d, 3.1d, 4.0d, -2.1d, 22d, 11.7d, 3d, 14d};
+ double sigma = populationStandardDeviation(values);
+ SecondMoment m = new SecondMoment();
+ m.evaluate(values); // side effect is to add values
+ StandardDeviation s1 = new StandardDeviation();
+ s1.setBiasCorrected(false);
+ assertEquals(sigma, s1.evaluate(values), 1E-14);
+ s1.incrementAll(values);
+ assertEquals(sigma, s1.getResult(), 1E-14);
+ s1 = new StandardDeviation(false, m);
+ assertEquals(sigma, s1.getResult(), 1E-14);
+ s1 = new StandardDeviation(false);
+ assertEquals(sigma, s1.evaluate(values), 1E-14);
+ s1.incrementAll(values);
+ assertEquals(sigma, s1.getResult(), 1E-14);
+ }
+
+ /**
+ * Definitional formula for population standard deviation
+ */
+ protected double populationStandardDeviation(double[] v) {
+ double mean = new Mean().evaluate(v);
+ double sum = 0;
+ for (int i = 0; i < v.length; i++) {
+ sum += (v[i] - mean) * (v[i] - mean);
+ }
+ return FastMath.sqrt(sum / v.length);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/ThirdMomentTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/ThirdMomentTest.java
new file mode 100644
index 0000000..f376c53
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/ThirdMomentTest.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.stat.descriptive.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link ThirdMoment} class.
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class ThirdMomentTest extends StorelessUnivariateStatisticAbstractTest{
+
+ /** descriptive statistic. */
+ protected ThirdMoment stat;
+
+ /**
+ * @param name
+ */
+ public ThirdMomentTest(String name) {
+ super(name);
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#getUnivariateStatistic()
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new ThirdMoment();
+ }
+
+ /**
+ * @see org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest#expectedValue()
+ */
+ @Override
+ public double expectedValue() {
+ return this.thirdMoment;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/VarianceTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VarianceTest.java
new file mode 100644
index 0000000..7da38ed
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VarianceTest.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.moment;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.util.MathUtils;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class VarianceTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Variance stat;
+
+ /**
+ * @param name
+ */
+ public VarianceTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Variance();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.var;
+ }
+
+ /**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
+ public double expectedWeightedValue() {
+ return this.weightedVar;
+ }
+
+ /**
+ * Make sure Double.NaN is returned iff n = 0
+ *
+ */
+ public void testNaN() {
+ StandardDeviation std = new StandardDeviation();
+ assertTrue(Double.isNaN(std.getResult()));
+ std.increment(1d);
+ assertEquals(0d, std.getResult(), 0);
+ }
+
+ /**
+ * Test population version of variance
+ */
+ public void testPopulation() {
+ double[] values = {-1.0d, 3.1d, 4.0d, -2.1d, 22d, 11.7d, 3d, 14d};
+ SecondMoment m = new SecondMoment();
+ m.evaluate(values); // side effect is to add values
+ Variance v1 = new Variance();
+ v1.setBiasCorrected(false);
+ assertEquals(populationVariance(values), v1.evaluate(values), 1E-14);
+ v1.incrementAll(values);
+ assertEquals(populationVariance(values), v1.getResult(), 1E-14);
+ v1 = new Variance(false, m);
+ assertEquals(populationVariance(values), v1.getResult(), 1E-14);
+ v1 = new Variance(false);
+ assertEquals(populationVariance(values), v1.evaluate(values), 1E-14);
+ v1.incrementAll(values);
+ assertEquals(populationVariance(values), v1.getResult(), 1E-14);
+ }
+
+ /**
+ * Definitional formula for population variance
+ */
+ protected double populationVariance(double[] v) {
+ double mean = new Mean().evaluate(v);
+ double sum = 0;
+ for (int i = 0; i < v.length; i++) {
+ sum += (v[i] - mean) * (v[i] - mean);
+ }
+ return sum / v.length;
+ }
+
+ public void testWeightedVariance() {
+ Variance variance = new Variance();
+ assertEquals(expectedWeightedValue(),
+ variance.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
+
+ // All weights = 1 -> weighted variance = unweighted variance
+ assertEquals(expectedValue(),
+ variance.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
+
+ // All weights the same -> when weights are normalized to sum to the length of the values array,
+ // weighted variance = unweighted value
+ assertEquals(expectedValue(),
+ variance.evaluate(testArray, MathUtils.normalizeArray(identicalWeightsArray, testArray.length),
+ 0, testArray.length), getTolerance());
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovarianceTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovarianceTest.java
new file mode 100644
index 0000000..74c60bd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialCovarianceTest.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.stat.descriptive.moment;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.RealMatrix;
+
+public class VectorialCovarianceTest
+extends TestCase {
+
+ public VectorialCovarianceTest(String name) {
+ super(name);
+ points = null;
+ }
+
+ public void testMismatch() {
+ try {
+ new VectorialCovariance(8, true).increment(new double[5]);
+ fail("an exception should have been thrown");
+ } catch (DimensionMismatchException dme) {
+ assertEquals(5, dme.getDimension1());
+ assertEquals(8, dme.getDimension2());
+ }
+ }
+
+ public void testSimplistic() throws DimensionMismatchException {
+ VectorialCovariance stat = new VectorialCovariance(2, true);
+ stat.increment(new double[] {-1.0, 1.0});
+ stat.increment(new double[] { 1.0, -1.0});
+ RealMatrix c = stat.getResult();
+ assertEquals( 2.0, c.getEntry(0, 0), 1.0e-12);
+ assertEquals(-2.0, c.getEntry(1, 0), 1.0e-12);
+ assertEquals( 2.0, c.getEntry(1, 1), 1.0e-12);
+ }
+
+ public void testBasicStats() throws DimensionMismatchException {
+
+ VectorialCovariance stat = new VectorialCovariance(points[0].length, true);
+ for (int i = 0; i < points.length; ++i) {
+ stat.increment(points[i]);
+ }
+
+ assertEquals(points.length, stat.getN());
+
+ RealMatrix c = stat.getResult();
+ double[][] refC = new double[][] {
+ { 8.0470, -1.9195, -3.4445},
+ {-1.9195, 1.0470, 3.2795},
+ {-3.4445, 3.2795, 12.2070}
+ };
+
+ for (int i = 0; i < c.getRowDimension(); ++i) {
+ for (int j = 0; j <= i; ++j) {
+ assertEquals(refC[i][j], c.getEntry(i, j), 1.0e-12);
+ }
+ }
+
+ }
+
+ public void testSerial(){
+ VectorialCovariance stat = new VectorialCovariance(points[0].length, true);
+ assertEquals(stat, TestUtils.serializeAndRecover(stat));
+ }
+
+ @Override
+ public void setUp() {
+ points = new double[][] {
+ { 1.2, 2.3, 4.5},
+ {-0.7, 2.3, 5.0},
+ { 3.1, 0.0, -3.1},
+ { 6.0, 1.2, 4.2},
+ {-0.7, 2.3, 5.0}
+ };
+ }
+
+ @Override
+ public void tearDown() {
+ points = null;
+ }
+
+ private double [][] points;
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialMeanTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialMeanTest.java
new file mode 100644
index 0000000..a8b9ae8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/moment/VectorialMeanTest.java
@@ -0,0 +1,95 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements. See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership. The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License. You may obtain a copy of the License at
+
+//http://www.apache.org/licenses/LICENSE-2.0
+
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied. See the License for the
+//specific language governing permissions and limitations
+//under the License.
+
+package org.apache.commons.math.stat.descriptive.moment;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.DimensionMismatchException;
+import org.apache.commons.math.TestUtils;
+
+public class VectorialMeanTest
+extends TestCase {
+
+ public VectorialMeanTest(String name) {
+ super(name);
+ points = null;
+ }
+
+ public void testMismatch() {
+ try {
+ new VectorialMean(8).increment(new double[5]);
+ fail("an exception should have been thrown");
+ } catch (DimensionMismatchException dme) {
+ assertEquals(5, dme.getDimension1());
+ assertEquals(8, dme.getDimension2());
+ }
+ }
+
+ public void testSimplistic() throws DimensionMismatchException {
+ VectorialMean stat = new VectorialMean(2);
+ stat.increment(new double[] {-1.0, 1.0});
+ stat.increment(new double[] { 1.0, -1.0});
+ double[] mean = stat.getResult();
+ assertEquals(0.0, mean[0], 1.0e-12);
+ assertEquals(0.0, mean[1], 1.0e-12);
+ }
+
+ public void testBasicStats() throws DimensionMismatchException {
+
+ VectorialMean stat = new VectorialMean(points[0].length);
+ for (int i = 0; i < points.length; ++i) {
+ stat.increment(points[i]);
+ }
+
+ assertEquals(points.length, stat.getN());
+
+ double[] mean = stat.getResult();
+ double[] refMean = new double[] { 1.78, 1.62, 3.12};
+
+ for (int i = 0; i < mean.length; ++i) {
+ assertEquals(refMean[i], mean[i], 1.0e-12);
+ }
+
+ }
+
+ public void testSerial() throws DimensionMismatchException {
+ VectorialMean stat = new VectorialMean(points[0].length);
+ for (int i = 0; i < points.length; ++i) {
+ stat.increment(points[i]);
+ }
+ assertEquals(stat, TestUtils.serializeAndRecover(stat));
+ }
+ @Override
+ public void setUp() {
+ points = new double[][] {
+ { 1.2, 2.3, 4.5},
+ {-0.7, 2.3, 5.0},
+ { 3.1, 0.0, -3.1},
+ { 6.0, 1.2, 4.2},
+ {-0.7, 2.3, 5.0}
+ };
+ }
+
+ @Override
+ public void tearDown() {
+ points = null;
+ }
+
+ private double [][] points;
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/rank/MaxTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MaxTest.java
new file mode 100644
index 0000000..7482e61
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MaxTest.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.stat.descriptive.rank;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class MaxTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Max stat;
+
+ /**
+ * @param name
+ */
+ public MaxTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Max();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.max;
+ }
+
+ public void testSpecialValues() {
+ double[] testArray = {0d, Double.NaN, Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY};
+ Max max = new Max();
+ assertTrue(Double.isNaN(max.getResult()));
+ max.increment(testArray[0]);
+ assertEquals(0d, max.getResult(), 0);
+ max.increment(testArray[1]);
+ assertEquals(0d, max.getResult(), 0);
+ max.increment(testArray[2]);
+ assertEquals(0d, max.getResult(), 0);
+ max.increment(testArray[3]);
+ assertEquals(Double.POSITIVE_INFINITY, max.getResult(), 0);
+ assertEquals(Double.POSITIVE_INFINITY, max.evaluate(testArray), 0);
+ }
+
+ public void testNaNs() {
+ Max max = new Max();
+ double nan = Double.NaN;
+ assertEquals(3d, max.evaluate(new double[]{nan, 2d, 3d}), 0);
+ assertEquals(3d, max.evaluate(new double[]{1d, nan, 3d}), 0);
+ assertEquals(2d, max.evaluate(new double[]{1d, 2d, nan}), 0);
+ assertTrue(Double.isNaN(max.evaluate(new double[]{nan, nan, nan})));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/rank/MedianTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MedianTest.java
new file mode 100644
index 0000000..8ae2a6e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MedianTest.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.rank;
+
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class MedianTest extends UnivariateStatisticAbstractTest{
+
+ protected Median stat;
+
+ /**
+ * @param name
+ */
+ public MedianTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Median();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.median;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/rank/MinTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MinTest.java
new file mode 100644
index 0000000..6b5f489
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/rank/MinTest.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.stat.descriptive.rank;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class MinTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Min stat;
+
+ /**
+ * @param name
+ */
+ public MinTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Min();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.min;
+ }
+
+ public void testSpecialValues() {
+ double[] testArray = {0d, Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY};
+ Min min = new Min();
+ assertTrue(Double.isNaN(min.getResult()));
+ min.increment(testArray[0]);
+ assertEquals(0d, min.getResult(), 0);
+ min.increment(testArray[1]);
+ assertEquals(0d, min.getResult(), 0);
+ min.increment(testArray[2]);
+ assertEquals(0d, min.getResult(), 0);
+ min.increment(testArray[3]);
+ assertEquals(Double.NEGATIVE_INFINITY, min.getResult(), 0);
+ assertEquals(Double.NEGATIVE_INFINITY, min.evaluate(testArray), 0);
+ }
+
+ public void testNaNs() {
+ Min min = new Min();
+ double nan = Double.NaN;
+ assertEquals(2d, min.evaluate(new double[]{nan, 2d, 3d}), 0);
+ assertEquals(1d, min.evaluate(new double[]{1d, nan, 3d}), 0);
+ assertEquals(1d, min.evaluate(new double[]{1d, 2d, nan}), 0);
+ assertTrue(Double.isNaN(min.evaluate(new double[]{nan, nan, nan})));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/rank/PercentileTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/rank/PercentileTest.java
new file mode 100644
index 0000000..0f8624a
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/rank/PercentileTest.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.stat.descriptive.rank;
+
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math.stat.descriptive.UnivariateStatisticAbstractTest;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class PercentileTest extends UnivariateStatisticAbstractTest{
+
+ protected Percentile stat;
+
+ /**
+ * @param name
+ */
+ public PercentileTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Percentile(95.0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.percentile95;
+ }
+
+ public void testHighPercentile(){
+ double[] d = new double[]{1, 2, 3};
+ Percentile p = new Percentile(75);
+ assertEquals(3.0, p.evaluate(d), 1.0e-5);
+ }
+
+ public void testPercentile() {
+ double[] d = new double[] {1, 3, 2, 4};
+ Percentile p = new Percentile(30);
+ assertEquals(1.5, p.evaluate(d), 1.0e-5);
+ p.setQuantile(25);
+ assertEquals(1.25, p.evaluate(d), 1.0e-5);
+ p.setQuantile(75);
+ assertEquals(3.75, p.evaluate(d), 1.0e-5);
+ p.setQuantile(50);
+ assertEquals(2.5, p.evaluate(d), 1.0e-5);
+
+ // invalid percentiles
+ try {
+ p.evaluate(d, 0, d.length, -1.0);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ try {
+ p.evaluate(d, 0, d.length, 101.0);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+ }
+
+ public void testNISTExample() {
+ double[] d = new double[] {95.1772, 95.1567, 95.1937, 95.1959,
+ 95.1442, 95.0610, 95.1591, 95.1195, 95.1772, 95.0925, 95.1990, 95.1682
+ };
+ Percentile p = new Percentile(90);
+ assertEquals(95.1981, p.evaluate(d), 1.0e-4);
+ assertEquals(95.1990, p.evaluate(d,0,d.length, 100d), 0);
+ }
+
+ public void test5() {
+ Percentile percentile = new Percentile(5);
+ assertEquals(this.percentile5, percentile.evaluate(testArray), getTolerance());
+ }
+
+ public void testNullEmpty() {
+ Percentile percentile = new Percentile(50);
+ double[] nullArray = null;
+ double[] emptyArray = new double[] {};
+ try {
+ percentile.evaluate(nullArray);
+ fail("Expecting IllegalArgumentException for null array");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ assertTrue(Double.isNaN(percentile.evaluate(emptyArray)));
+ }
+
+ public void testSingleton() {
+ Percentile percentile = new Percentile(50);
+ double[] singletonArray = new double[] {1d};
+ assertEquals(1d, percentile.evaluate(singletonArray), 0);
+ assertEquals(1d, percentile.evaluate(singletonArray, 0, 1), 0);
+ assertEquals(1d, percentile.evaluate(singletonArray, 0, 1, 5), 0);
+ assertEquals(1d, percentile.evaluate(singletonArray, 0, 1, 100), 0);
+ assertTrue(Double.isNaN(percentile.evaluate(singletonArray, 0, 0)));
+ }
+
+ public void testSpecialValues() {
+ Percentile percentile = new Percentile(50);
+ double[] specialValues = new double[] {0d, 1d, 2d, 3d, 4d, Double.NaN};
+ assertEquals(2.5d, percentile.evaluate(specialValues), 0);
+ specialValues = new double[] {Double.NEGATIVE_INFINITY, 1d, 2d, 3d,
+ Double.NaN, Double.POSITIVE_INFINITY};
+ assertEquals(2.5d, percentile.evaluate(specialValues), 0);
+ specialValues = new double[] {1d, 1d, Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY};
+ assertTrue(Double.isInfinite(percentile.evaluate(specialValues)));
+ specialValues = new double[] {1d, 1d, Double.NaN,
+ Double.NaN};
+ assertTrue(Double.isNaN(percentile.evaluate(specialValues)));
+ specialValues = new double[] {1d, 1d, Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY};
+ // Interpolation results in NEGATIVE_INFINITY + POSITIVE_INFINITY
+ assertTrue(Double.isNaN(percentile.evaluate(specialValues)));
+ }
+
+ public void testSetQuantile() {
+ Percentile percentile = new Percentile(10);
+ percentile.setQuantile(100); // OK
+ assertEquals(100, percentile.getQuantile(), 0);
+ try {
+ percentile.setQuantile(0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ new Percentile(0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/summary/ProductTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/summary/ProductTest.java
new file mode 100644
index 0000000..4da006c
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/summary/ProductTest.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.stat.descriptive.summary;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class ProductTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Product stat;
+
+ /**
+ * @param name
+ */
+ public ProductTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Product();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getTolerance() {
+ return 10E8; //sic -- big absolute error due to only 15 digits of accuracy in double
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.product;
+ }
+
+ /**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
+ public double expectedWeightedValue() {
+ return this.weightedProduct;
+ }
+
+ public void testSpecialValues() {
+ Product product = new Product();
+ assertTrue(Double.isNaN(product.getResult()));
+ product.increment(1);
+ assertEquals(1, product.getResult(), 0);
+ product.increment(Double.POSITIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, product.getResult(), 0);
+ product.increment(Double.NEGATIVE_INFINITY);
+ assertEquals(Double.NEGATIVE_INFINITY, product.getResult(), 0);
+ product.increment(Double.NaN);
+ assertTrue(Double.isNaN(product.getResult()));
+ product.increment(1);
+ assertTrue(Double.isNaN(product.getResult()));
+ }
+
+ public void testWeightedProduct() {
+ Product product = new Product();
+ assertEquals(expectedWeightedValue(), product.evaluate(testArray, testWeightsArray, 0, testArray.length),getTolerance());
+ assertEquals(expectedValue(), product.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumLogTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumLogTest.java
new file mode 100644
index 0000000..567388d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumLogTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.descriptive.summary;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link UnivariateStatistic} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class SumLogTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected SumOfLogs stat;
+
+ /**
+ * @param name
+ */
+ public SumLogTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new SumOfLogs();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.sumLog;
+ }
+
+ public void testSpecialValues() {
+ SumOfLogs sum = new SumOfLogs();
+ // empty
+ assertTrue(Double.isNaN(sum.getResult()));
+
+ // finite data
+ sum.increment(1d);
+ assertFalse(Double.isNaN(sum.getResult()));
+
+ // add negative infinity
+ sum.increment(0d);
+ assertEquals(Double.NEGATIVE_INFINITY, sum.getResult(), 0);
+
+ // add positive infinity -- should make NaN
+ sum.increment(Double.POSITIVE_INFINITY);
+ assertTrue(Double.isNaN(sum.getResult()));
+
+ // clear
+ sum.clear();
+ assertTrue(Double.isNaN(sum.getResult()));
+
+ // positive infinity by itself
+ sum.increment(Double.POSITIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, sum.getResult(), 0);
+
+ // negative value -- should make NaN
+ sum.increment(-2d);
+ assertTrue(Double.isNaN(sum.getResult()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumSqTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumSqTest.java
new file mode 100644
index 0000000..9b14e47
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumSqTest.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.stat.descriptive.summary;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link SumOfSquares} class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class SumSqTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected SumOfSquares stat;
+
+ /**
+ * @param name
+ */
+ public SumSqTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new SumOfSquares();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.sumSq;
+ }
+
+ public void testSpecialValues() {
+ SumOfSquares sumSq = new SumOfSquares();
+ assertTrue(Double.isNaN(sumSq.getResult()));
+ sumSq.increment(2d);
+ assertEquals(4d, sumSq.getResult(), 0);
+ sumSq.increment(Double.POSITIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, sumSq.getResult(), 0);
+ sumSq.increment(Double.NEGATIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, sumSq.getResult(), 0);
+ sumSq.increment(Double.NaN);
+ assertTrue(Double.isNaN(sumSq.getResult()));
+ sumSq.increment(1);
+ assertTrue(Double.isNaN(sumSq.getResult()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumTest.java b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumTest.java
new file mode 100644
index 0000000..07327bb
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/descriptive/summary/SumTest.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.stat.descriptive.summary;
+
+import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatisticAbstractTest;
+import org.apache.commons.math.stat.descriptive.UnivariateStatistic;
+
+/**
+ * Test cases for the {@link Sum} class.
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+public class SumTest extends StorelessUnivariateStatisticAbstractTest{
+
+ protected Sum stat;
+
+ /**
+ * @param name
+ */
+ public SumTest(String name) {
+ super(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public UnivariateStatistic getUnivariateStatistic() {
+ return new Sum();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double expectedValue() {
+ return this.sum;
+ }
+
+ /**Expected value for the testArray defined in UnivariateStatisticAbstractTest */
+ public double expectedWeightedValue() {
+ return this.weightedSum;
+ }
+
+ public void testSpecialValues() {
+ Sum sum = new Sum();
+ assertTrue(Double.isNaN(sum.getResult()));
+ sum.increment(1);
+ assertEquals(1, sum.getResult(), 0);
+ sum.increment(Double.POSITIVE_INFINITY);
+ assertEquals(Double.POSITIVE_INFINITY, sum.getResult(), 0);
+ sum.increment(Double.NEGATIVE_INFINITY);
+ assertTrue(Double.isNaN(sum.getResult()));
+ sum.increment(1);
+ assertTrue(Double.isNaN(sum.getResult()));
+ }
+
+ public void testWeightedSum() {
+ Sum sum = new Sum();
+ assertEquals(expectedWeightedValue(), sum.evaluate(testArray, testWeightsArray, 0, testArray.length), getTolerance());
+ assertEquals(expectedValue(), sum.evaluate(testArray, unitWeightsArray, 0, testArray.length), getTolerance());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/ChiSquareFactoryTest.java b/src/test/java/org/apache/commons/math/stat/inference/ChiSquareFactoryTest.java
new file mode 100644
index 0000000..54bc18d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/ChiSquareFactoryTest.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.stat.inference;
+
+/**
+ * Test cases for the ChiSquareTestFactory.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class ChiSquareFactoryTest extends ChiSquareTestTest {
+
+ public ChiSquareFactoryTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ testStatistic = TestUtils.getUnknownDistributionChiSquareTest();
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/ChiSquareTestTest.java b/src/test/java/org/apache/commons/math/stat/inference/ChiSquareTestTest.java
new file mode 100644
index 0000000..776248d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/ChiSquareTestTest.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.inference;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the ChiSquareTestImpl class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class ChiSquareTestTest extends TestCase {
+
+ protected UnknownDistributionChiSquareTest testStatistic = new ChiSquareTestImpl();
+
+ public ChiSquareTestTest(String name) {
+ super(name);
+ }
+
+ public void testChiSquare() throws Exception {
+
+ // Target values computed using R version 1.8.1
+ // Some assembly required ;-)
+ // Use sum((obs - exp)^2/exp) for the chi-square statistic and
+ // 1 - pchisq(sum((obs - exp)^2/exp), length(obs) - 1) for the p-value
+
+ long[] observed = {10, 9, 11};
+ double[] expected = {10, 10, 10};
+ assertEquals("chi-square statistic", 0.2, testStatistic.chiSquare(expected, observed), 10E-12);
+ assertEquals("chi-square p-value", 0.904837418036, testStatistic.chiSquareTest(expected, observed), 1E-10);
+
+ long[] observed1 = { 500, 623, 72, 70, 31 };
+ double[] expected1 = { 485, 541, 82, 61, 37 };
+ assertEquals( "chi-square test statistic", 9.023307936427388, testStatistic.chiSquare(expected1, observed1), 1E-10);
+ assertEquals("chi-square p-value", 0.06051952647453607, testStatistic.chiSquareTest(expected1, observed1), 1E-9);
+ assertTrue("chi-square test reject", testStatistic.chiSquareTest(expected1, observed1, 0.08));
+ assertTrue("chi-square test accept", !testStatistic.chiSquareTest(expected1, observed1, 0.05));
+
+ try {
+ testStatistic.chiSquareTest(expected1, observed1, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ long[] tooShortObs = { 0 };
+ double[] tooShortEx = { 1 };
+ try {
+ testStatistic.chiSquare(tooShortEx, tooShortObs);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // unmatched arrays
+ long[] unMatchedObs = { 0, 1, 2, 3 };
+ double[] unMatchedEx = { 1, 1, 2 };
+ try {
+ testStatistic.chiSquare(unMatchedEx, unMatchedObs);
+ fail("arrays have different lengths, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // 0 expected count
+ expected[0] = 0;
+ try {
+ testStatistic.chiSquareTest(expected, observed, .01);
+ fail("bad expected count, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // negative observed count
+ expected[0] = 1;
+ observed[0] = -1;
+ try {
+ testStatistic.chiSquareTest(expected, observed, .01);
+ fail("bad expected count, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ }
+
+ public void testChiSquareIndependence() throws Exception {
+
+ // Target values computed using R version 1.8.1
+
+ long[][] counts = { {40, 22, 43}, {91, 21, 28}, {60, 10, 22}};
+ assertEquals( "chi-square test statistic", 22.709027688, testStatistic.chiSquare(counts), 1E-9);
+ assertEquals("chi-square p-value", 0.000144751460134, testStatistic.chiSquareTest(counts), 1E-9);
+ assertTrue("chi-square test reject", testStatistic.chiSquareTest(counts, 0.0002));
+ assertTrue("chi-square test accept", !testStatistic.chiSquareTest(counts, 0.0001));
+
+ long[][] counts2 = {{10, 15}, {30, 40}, {60, 90} };
+ assertEquals( "chi-square test statistic", 0.168965517241, testStatistic.chiSquare(counts2), 1E-9);
+ assertEquals("chi-square p-value",0.918987499852, testStatistic.chiSquareTest(counts2), 1E-9);
+ assertTrue("chi-square test accept", !testStatistic.chiSquareTest(counts2, 0.1));
+
+ // ragged input array
+ long[][] counts3 = { {40, 22, 43}, {91, 21, 28}, {60, 10}};
+ try {
+ testStatistic.chiSquare(counts3);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // insufficient data
+ long[][] counts4 = {{40, 22, 43}};
+ try {
+ testStatistic.chiSquare(counts4);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ long[][] counts5 = {{40}, {40}, {30}, {10}};
+ try {
+ testStatistic.chiSquare(counts5);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // negative counts
+ long[][] counts6 = {{10, -2}, {30, 40}, {60, 90} };
+ try {
+ testStatistic.chiSquare(counts6);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // bad alpha
+ try {
+ testStatistic.chiSquareTest(counts, 0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testChiSquareLargeTestStatistic() throws Exception {
+ double[] exp = new double[] {
+ 3389119.5, 649136.6, 285745.4, 25357364.76, 11291189.78, 543628.0,
+ 232921.0, 437665.75
+ };
+
+ long[] obs = new long[] {
+ 2372383, 584222, 257170, 17750155, 7903832, 489265, 209628, 393899
+ };
+ org.apache.commons.math.stat.inference.ChiSquareTestImpl csti =
+ new org.apache.commons.math.stat.inference.ChiSquareTestImpl();
+ double cst = csti.chiSquareTest(exp, obs);
+ assertEquals("chi-square p-value", 0.0, cst, 1E-3);
+ assertEquals( "chi-square test statistic",
+ 114875.90421929007, testStatistic.chiSquare(exp, obs), 1E-9);
+ }
+
+ /** Contingency table containing zeros - PR # 32531 */
+ public void testChiSquareZeroCount() throws Exception {
+ // Target values computed using R version 1.8.1
+ long[][] counts = { {40, 0, 4}, {91, 1, 2}, {60, 2, 0}};
+ assertEquals( "chi-square test statistic", 9.67444662263,
+ testStatistic.chiSquare(counts), 1E-9);
+ assertEquals("chi-square p-value", 0.0462835770603,
+ testStatistic.chiSquareTest(counts), 1E-9);
+ }
+
+ /** Target values verified using DATAPLOT version 2006.3 */
+ public void testChiSquareDataSetsComparisonEqualCounts()
+ throws Exception {
+ long[] observed1 = {10, 12, 12, 10};
+ long[] observed2 = {5, 15, 14, 10};
+ assertEquals("chi-square p value", 0.541096,
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2), 1E-6);
+ assertEquals("chi-square test statistic", 2.153846,
+ testStatistic.chiSquareDataSetsComparison(
+ observed1, observed2), 1E-6);
+ assertFalse("chi-square test result",
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2, 0.4));
+ }
+
+ /** Target values verified using DATAPLOT version 2006.3 */
+ public void testChiSquareDataSetsComparisonUnEqualCounts()
+ throws Exception {
+ long[] observed1 = {10, 12, 12, 10, 15};
+ long[] observed2 = {15, 10, 10, 15, 5};
+ assertEquals("chi-square p value", 0.124115,
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2), 1E-6);
+ assertEquals("chi-square test statistic", 7.232189,
+ testStatistic.chiSquareDataSetsComparison(
+ observed1, observed2), 1E-6);
+ assertTrue("chi-square test result",
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2, 0.13));
+ assertFalse("chi-square test result",
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2, 0.12));
+ }
+
+ public void testChiSquareDataSetsComparisonBadCounts()
+ throws Exception {
+ long[] observed1 = {10, -1, 12, 10, 15};
+ long[] observed2 = {15, 10, 10, 15, 5};
+ try {
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed1, observed2);
+ fail("Expecting IllegalArgumentException - negative count");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ long[] observed3 = {10, 0, 12, 10, 15};
+ long[] observed4 = {15, 0, 10, 15, 5};
+ try {
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed3, observed4);
+ fail("Expecting IllegalArgumentException - double 0's");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ long[] observed5 = {10, 10, 12, 10, 15};
+ long[] observed6 = {0, 0, 0, 0, 0};
+ try {
+ testStatistic.chiSquareTestDataSetsComparison(
+ observed5, observed6);
+ fail("Expecting IllegalArgumentException - vanishing counts");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/OneWayAnovaTest.java b/src/test/java/org/apache/commons/math/stat/inference/OneWayAnovaTest.java
new file mode 100644
index 0000000..5daaff0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/OneWayAnovaTest.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.inference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the OneWayAnovaImpl class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class OneWayAnovaTest extends TestCase {
+
+ protected OneWayAnova testStatistic = new OneWayAnovaImpl();
+
+ private double[] emptyArray = {};
+
+ private double[] classA =
+ {93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0 };
+ private double[] classB =
+ {99.0, 92.0, 102.0, 100.0, 102.0, 89.0 };
+ private double[] classC =
+ {110.0, 115.0, 111.0, 117.0, 128.0, 117.0 };
+
+ public OneWayAnovaTest(String name) {
+ super(name);
+ }
+
+ public void testAnovaFValue() throws Exception {
+ // Target comparison values computed using R version 2.6.0 (Linux version)
+ List<double[]> threeClasses = new ArrayList<double[]>();
+ threeClasses.add(classA);
+ threeClasses.add(classB);
+ threeClasses.add(classC);
+
+ assertEquals("ANOVA F-value", 24.67361709460624,
+ testStatistic.anovaFValue(threeClasses), 1E-12);
+
+ List<double[]> twoClasses = new ArrayList<double[]>();
+ twoClasses.add(classA);
+ twoClasses.add(classB);
+
+ assertEquals("ANOVA F-value", 0.0150579150579,
+ testStatistic.anovaFValue(twoClasses), 1E-12);
+
+ List<double[]> emptyContents = new ArrayList<double[]>();
+ emptyContents.add(emptyArray);
+ emptyContents.add(classC);
+ try {
+ testStatistic.anovaFValue(emptyContents);
+ fail("empty array for key classX, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ List<double[]> tooFew = new ArrayList<double[]>();
+ tooFew.add(classA);
+ try {
+ testStatistic.anovaFValue(tooFew);
+ fail("less than two classes, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+
+ public void testAnovaPValue() throws Exception {
+ // Target comparison values computed using R version 2.6.0 (Linux version)
+ List<double[]> threeClasses = new ArrayList<double[]>();
+ threeClasses.add(classA);
+ threeClasses.add(classB);
+ threeClasses.add(classC);
+
+ assertEquals("ANOVA P-value", 6.959446E-06,
+ testStatistic.anovaPValue(threeClasses), 1E-12);
+
+ List<double[]> twoClasses = new ArrayList<double[]>();
+ twoClasses.add(classA);
+ twoClasses.add(classB);
+
+ assertEquals("ANOVA P-value", 0.904212960464,
+ testStatistic.anovaPValue(twoClasses), 1E-12);
+
+ }
+
+ public void testAnovaTest() throws Exception {
+ // Target comparison values computed using R version 2.3.1 (Linux version)
+ List<double[]> threeClasses = new ArrayList<double[]>();
+ threeClasses.add(classA);
+ threeClasses.add(classB);
+ threeClasses.add(classC);
+
+ assertTrue("ANOVA Test P<0.01", testStatistic.anovaTest(threeClasses, 0.01));
+
+ List<double[]> twoClasses = new ArrayList<double[]>();
+ twoClasses.add(classA);
+ twoClasses.add(classB);
+
+ assertFalse("ANOVA Test P>0.01", testStatistic.anovaTest(twoClasses, 0.01));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/TTestFactoryTest.java b/src/test/java/org/apache/commons/math/stat/inference/TTestFactoryTest.java
new file mode 100644
index 0000000..a1fdeea
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/TTestFactoryTest.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.stat.inference;
+
+/**
+ * Test cases for the TTestTestFactory.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class TTestFactoryTest extends TTestTest {
+
+ public TTestFactoryTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ testStatistic = TestUtils.getTTest();
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/TTestTest.java b/src/test/java/org/apache/commons/math/stat/inference/TTestTest.java
new file mode 100644
index 0000000..1bf9c79
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/TTestTest.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.stat.inference;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+/**
+ * Test cases for the TTestImpl class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class TTestTest extends TestCase {
+
+ protected TTest testStatistic = new TTestImpl();
+
+ private double[] tooShortObs = { 1.0 };
+ private double[] emptyObs = {};
+ private SummaryStatistics emptyStats = new SummaryStatistics();
+ SummaryStatistics tooShortStats = null;
+
+ public TTestTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ tooShortStats = new SummaryStatistics();
+ tooShortStats.addValue(0d);
+ }
+
+ public void testOneSampleT() throws Exception {
+ double[] observed =
+ {93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0, 88.0, 98.0, 94.0, 101.0, 92.0, 95.0 };
+ double mu = 100.0;
+ SummaryStatistics sampleStats = null;
+ sampleStats = new SummaryStatistics();
+ for (int i = 0; i < observed.length; i++) {
+ sampleStats.addValue(observed[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("t statistic", -2.81976445346,
+ testStatistic.t(mu, observed), 10E-10);
+ assertEquals("t statistic", -2.81976445346,
+ testStatistic.t(mu, sampleStats), 10E-10);
+ assertEquals("p value", 0.0136390585873,
+ testStatistic.tTest(mu, observed), 10E-10);
+ assertEquals("p value", 0.0136390585873,
+ testStatistic.tTest(mu, sampleStats), 10E-10);
+
+ try {
+ testStatistic.t(mu, (double[]) null);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(mu, (SummaryStatistics) null);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(mu, emptyObs);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(mu, emptyStats);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(mu, tooShortObs);
+ fail("insufficient data to compute t statistic, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.tTest(mu, tooShortObs);
+ fail("insufficient data to perform t test, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(mu, tooShortStats);
+ fail("insufficient data to compute t statistic, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ testStatistic.tTest(mu, tooShortStats);
+ fail("insufficient data to perform t test, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testOneSampleTTest() throws Exception {
+ double[] oneSidedP =
+ {2d, 0d, 6d, 6d, 3d, 3d, 2d, 3d, -6d, 6d, 6d, 6d, 3d, 0d, 1d, 1d, 0d, 2d, 3d, 3d };
+ SummaryStatistics oneSidedPStats = new SummaryStatistics();
+ for (int i = 0; i < oneSidedP.length; i++) {
+ oneSidedPStats.addValue(oneSidedP[i]);
+ }
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("one sample t stat", 3.86485535541,
+ testStatistic.t(0d, oneSidedP), 10E-10);
+ assertEquals("one sample t stat", 3.86485535541,
+ testStatistic.t(0d, oneSidedPStats),1E-10);
+ assertEquals("one sample p value", 0.000521637019637,
+ testStatistic.tTest(0d, oneSidedP) / 2d, 10E-10);
+ assertEquals("one sample p value", 0.000521637019637,
+ testStatistic.tTest(0d, oneSidedPStats) / 2d, 10E-5);
+ assertTrue("one sample t-test reject", testStatistic.tTest(0d, oneSidedP, 0.01));
+ assertTrue("one sample t-test reject", testStatistic.tTest(0d, oneSidedPStats, 0.01));
+ assertTrue("one sample t-test accept", !testStatistic.tTest(0d, oneSidedP, 0.0001));
+ assertTrue("one sample t-test accept", !testStatistic.tTest(0d, oneSidedPStats, 0.0001));
+
+ try {
+ testStatistic.tTest(0d, oneSidedP, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(0d, oneSidedPStats, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ }
+
+ public void testTwoSampleTHeterscedastic() throws Exception {
+ double[] sample1 = { 7d, -4d, 18d, 17d, -3d, -5d, 1d, 10d, 11d, -2d };
+ double[] sample2 = { -1d, 12d, -1d, -3d, 3d, -5d, 5d, 2d, -11d, -1d, -3d };
+ SummaryStatistics sampleStats1 = new SummaryStatistics();
+ for (int i = 0; i < sample1.length; i++) {
+ sampleStats1.addValue(sample1[i]);
+ }
+ SummaryStatistics sampleStats2 = new SummaryStatistics();
+ for (int i = 0; i < sample2.length; i++) {
+ sampleStats2.addValue(sample2[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("two sample heteroscedastic t stat", 1.60371728768,
+ testStatistic.t(sample1, sample2), 1E-10);
+ assertEquals("two sample heteroscedastic t stat", 1.60371728768,
+ testStatistic.t(sampleStats1, sampleStats2), 1E-10);
+ assertEquals("two sample heteroscedastic p value", 0.128839369622,
+ testStatistic.tTest(sample1, sample2), 1E-10);
+ assertEquals("two sample heteroscedastic p value", 0.128839369622,
+ testStatistic.tTest(sampleStats1, sampleStats2), 1E-10);
+ assertTrue("two sample heteroscedastic t-test reject",
+ testStatistic.tTest(sample1, sample2, 0.2));
+ assertTrue("two sample heteroscedastic t-test reject",
+ testStatistic.tTest(sampleStats1, sampleStats2, 0.2));
+ assertTrue("two sample heteroscedastic t-test accept",
+ !testStatistic.tTest(sample1, sample2, 0.1));
+ assertTrue("two sample heteroscedastic t-test accept",
+ !testStatistic.tTest(sampleStats1, sampleStats2, 0.1));
+
+ try {
+ testStatistic.tTest(sample1, sample2, .95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(sampleStats1, sampleStats2, .95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(sample1, tooShortObs, .01);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(sampleStats1, tooShortStats, .01);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(sample1, tooShortObs);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.tTest(sampleStats1, tooShortStats);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(sample1, tooShortObs);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ testStatistic.t(sampleStats1, tooShortStats);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+ public void testTwoSampleTHomoscedastic() throws Exception {
+ double[] sample1 ={2, 4, 6, 8, 10, 97};
+ double[] sample2 = {4, 6, 8, 10, 16};
+ SummaryStatistics sampleStats1 = new SummaryStatistics();
+ for (int i = 0; i < sample1.length; i++) {
+ sampleStats1.addValue(sample1[i]);
+ }
+ SummaryStatistics sampleStats2 = new SummaryStatistics();
+ for (int i = 0; i < sample2.length; i++) {
+ sampleStats2.addValue(sample2[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("two sample homoscedastic t stat", 0.73096310086,
+ testStatistic.homoscedasticT(sample1, sample2), 10E-11);
+ assertEquals("two sample homoscedastic p value", 0.4833963785,
+ testStatistic.homoscedasticTTest(sampleStats1, sampleStats2), 1E-10);
+ assertTrue("two sample homoscedastic t-test reject",
+ testStatistic.homoscedasticTTest(sample1, sample2, 0.49));
+ assertTrue("two sample homoscedastic t-test accept",
+ !testStatistic.homoscedasticTTest(sample1, sample2, 0.48));
+ }
+
+ public void testSmallSamples() throws Exception {
+ double[] sample1 = {1d, 3d};
+ double[] sample2 = {4d, 5d};
+
+ // Target values computed using R, version 1.8.1 (linux version)
+ assertEquals(-2.2360679775, testStatistic.t(sample1, sample2),
+ 1E-10);
+ assertEquals(0.198727388935, testStatistic.tTest(sample1, sample2),
+ 1E-10);
+ }
+
+ public void testPaired() throws Exception {
+ double[] sample1 = {1d, 3d, 5d, 7d};
+ double[] sample2 = {0d, 6d, 11d, 2d};
+ double[] sample3 = {5d, 7d, 8d, 10d};
+
+ // Target values computed using R, version 1.8.1 (linux version)
+ assertEquals(-0.3133, testStatistic.pairedT(sample1, sample2), 1E-4);
+ assertEquals(0.774544295819, testStatistic.pairedTTest(sample1, sample2), 1E-10);
+ assertEquals(0.001208, testStatistic.pairedTTest(sample1, sample3), 1E-6);
+ assertFalse(testStatistic.pairedTTest(sample1, sample3, .001));
+ assertTrue(testStatistic.pairedTTest(sample1, sample3, .002));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/inference/TestUtilsTest.java b/src/test/java/org/apache/commons/math/stat/inference/TestUtilsTest.java
new file mode 100644
index 0000000..03c351d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/inference/TestUtilsTest.java
@@ -0,0 +1,458 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.stat.descriptive.SummaryStatistics;
+/**
+ * Test cases for the TestUtils class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public class TestUtilsTest extends TestCase {
+
+ public TestUtilsTest(String name) {
+ super(name);
+ }
+
+ public void testChiSquare() throws Exception {
+
+ // Target values computed using R version 1.8.1
+ // Some assembly required ;-)
+ // Use sum((obs - exp)^2/exp) for the chi-square statistic and
+ // 1 - pchisq(sum((obs - exp)^2/exp), length(obs) - 1) for the p-value
+
+ long[] observed = {10, 9, 11};
+ double[] expected = {10, 10, 10};
+ assertEquals("chi-square statistic", 0.2, TestUtils.chiSquare(expected, observed), 10E-12);
+ assertEquals("chi-square p-value", 0.904837418036, TestUtils.chiSquareTest(expected, observed), 1E-10);
+
+ long[] observed1 = { 500, 623, 72, 70, 31 };
+ double[] expected1 = { 485, 541, 82, 61, 37 };
+ assertEquals( "chi-square test statistic", 9.023307936427388, TestUtils.chiSquare(expected1, observed1), 1E-10);
+ assertEquals("chi-square p-value", 0.06051952647453607, TestUtils.chiSquareTest(expected1, observed1), 1E-9);
+ assertTrue("chi-square test reject", TestUtils.chiSquareTest(expected1, observed1, 0.07));
+ assertTrue("chi-square test accept", !TestUtils.chiSquareTest(expected1, observed1, 0.05));
+
+ try {
+ TestUtils.chiSquareTest(expected1, observed1, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ long[] tooShortObs = { 0 };
+ double[] tooShortEx = { 1 };
+ try {
+ TestUtils.chiSquare(tooShortEx, tooShortObs);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // unmatched arrays
+ long[] unMatchedObs = { 0, 1, 2, 3 };
+ double[] unMatchedEx = { 1, 1, 2 };
+ try {
+ TestUtils.chiSquare(unMatchedEx, unMatchedObs);
+ fail("arrays have different lengths, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // 0 expected count
+ expected[0] = 0;
+ try {
+ TestUtils.chiSquareTest(expected, observed, .01);
+ fail("bad expected count, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // negative observed count
+ expected[0] = 1;
+ observed[0] = -1;
+ try {
+ TestUtils.chiSquareTest(expected, observed, .01);
+ fail("bad expected count, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ }
+
+ public void testChiSquareIndependence() throws Exception {
+
+ // Target values computed using R version 1.8.1
+
+ long[][] counts = { {40, 22, 43}, {91, 21, 28}, {60, 10, 22}};
+ assertEquals( "chi-square test statistic", 22.709027688, TestUtils.chiSquare(counts), 1E-9);
+ assertEquals("chi-square p-value", 0.000144751460134, TestUtils.chiSquareTest(counts), 1E-9);
+ assertTrue("chi-square test reject", TestUtils.chiSquareTest(counts, 0.0002));
+ assertTrue("chi-square test accept", !TestUtils.chiSquareTest(counts, 0.0001));
+
+ long[][] counts2 = {{10, 15}, {30, 40}, {60, 90} };
+ assertEquals( "chi-square test statistic", 0.168965517241, TestUtils.chiSquare(counts2), 1E-9);
+ assertEquals("chi-square p-value",0.918987499852, TestUtils.chiSquareTest(counts2), 1E-9);
+ assertTrue("chi-square test accept", !TestUtils.chiSquareTest(counts2, 0.1));
+
+ // ragged input array
+ long[][] counts3 = { {40, 22, 43}, {91, 21, 28}, {60, 10}};
+ try {
+ TestUtils.chiSquare(counts3);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // insufficient data
+ long[][] counts4 = {{40, 22, 43}};
+ try {
+ TestUtils.chiSquare(counts4);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ long[][] counts5 = {{40}, {40}, {30}, {10}};
+ try {
+ TestUtils.chiSquare(counts5);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // negative counts
+ long[][] counts6 = {{10, -2}, {30, 40}, {60, 90} };
+ try {
+ TestUtils.chiSquare(counts6);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // bad alpha
+ try {
+ TestUtils.chiSquareTest(counts, 0);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testChiSquareLargeTestStatistic() throws Exception {
+ double[] exp = new double[] {
+ 3389119.5, 649136.6, 285745.4, 25357364.76, 11291189.78, 543628.0,
+ 232921.0, 437665.75
+ };
+
+ long[] obs = new long[] {
+ 2372383, 584222, 257170, 17750155, 7903832, 489265, 209628, 393899
+ };
+ org.apache.commons.math.stat.inference.ChiSquareTestImpl csti =
+ new org.apache.commons.math.stat.inference.ChiSquareTestImpl();
+ double cst = csti.chiSquareTest(exp, obs);
+ assertEquals("chi-square p-value", 0.0, cst, 1E-3);
+ assertEquals( "chi-square test statistic",
+ 114875.90421929007, TestUtils.chiSquare(exp, obs), 1E-9);
+ }
+
+ /** Contingency table containing zeros - PR # 32531 */
+ public void testChiSquareZeroCount() throws Exception {
+ // Target values computed using R version 1.8.1
+ long[][] counts = { {40, 0, 4}, {91, 1, 2}, {60, 2, 0}};
+ assertEquals( "chi-square test statistic", 9.67444662263,
+ TestUtils.chiSquare(counts), 1E-9);
+ assertEquals("chi-square p-value", 0.0462835770603,
+ TestUtils.chiSquareTest(counts), 1E-9);
+ }
+
+ private double[] tooShortObs = { 1.0 };
+ private double[] emptyObs = {};
+ private SummaryStatistics emptyStats = new SummaryStatistics();
+
+ public void testOneSampleT() throws Exception {
+ double[] observed =
+ {93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0, 88.0, 98.0, 94.0, 101.0, 92.0, 95.0 };
+ double mu = 100.0;
+ SummaryStatistics sampleStats = null;
+ sampleStats = new SummaryStatistics();
+ for (int i = 0; i < observed.length; i++) {
+ sampleStats.addValue(observed[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("t statistic", -2.81976445346,
+ TestUtils.t(mu, observed), 10E-10);
+ assertEquals("t statistic", -2.81976445346,
+ TestUtils.t(mu, sampleStats), 10E-10);
+ assertEquals("p value", 0.0136390585873,
+ TestUtils.tTest(mu, observed), 10E-10);
+ assertEquals("p value", 0.0136390585873,
+ TestUtils.tTest(mu, sampleStats), 10E-10);
+
+ try {
+ TestUtils.t(mu, (double[]) null);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(mu, (SummaryStatistics) null);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(mu, emptyObs);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(mu, emptyStats);
+ fail("arguments too short, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(mu, tooShortObs);
+ fail("insufficient data to compute t statistic, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ TestUtils.tTest(mu, tooShortObs);
+ fail("insufficient data to perform t test, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(mu, (SummaryStatistics) null);
+ fail("insufficient data to compute t statistic, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ TestUtils.tTest(mu, (SummaryStatistics) null);
+ fail("insufficient data to perform t test, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testOneSampleTTest() throws Exception {
+ double[] oneSidedP =
+ {2d, 0d, 6d, 6d, 3d, 3d, 2d, 3d, -6d, 6d, 6d, 6d, 3d, 0d, 1d, 1d, 0d, 2d, 3d, 3d };
+ SummaryStatistics oneSidedPStats = new SummaryStatistics();
+ for (int i = 0; i < oneSidedP.length; i++) {
+ oneSidedPStats.addValue(oneSidedP[i]);
+ }
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("one sample t stat", 3.86485535541,
+ TestUtils.t(0d, oneSidedP), 10E-10);
+ assertEquals("one sample t stat", 3.86485535541,
+ TestUtils.t(0d, oneSidedPStats),1E-10);
+ assertEquals("one sample p value", 0.000521637019637,
+ TestUtils.tTest(0d, oneSidedP) / 2d, 10E-10);
+ assertEquals("one sample p value", 0.000521637019637,
+ TestUtils.tTest(0d, oneSidedPStats) / 2d, 10E-5);
+ assertTrue("one sample t-test reject", TestUtils.tTest(0d, oneSidedP, 0.01));
+ assertTrue("one sample t-test reject", TestUtils.tTest(0d, oneSidedPStats, 0.01));
+ assertTrue("one sample t-test accept", !TestUtils.tTest(0d, oneSidedP, 0.0001));
+ assertTrue("one sample t-test accept", !TestUtils.tTest(0d, oneSidedPStats, 0.0001));
+
+ try {
+ TestUtils.tTest(0d, oneSidedP, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(0d, oneSidedPStats, 95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ }
+
+ public void testTwoSampleTHeterscedastic() throws Exception {
+ double[] sample1 = { 7d, -4d, 18d, 17d, -3d, -5d, 1d, 10d, 11d, -2d };
+ double[] sample2 = { -1d, 12d, -1d, -3d, 3d, -5d, 5d, 2d, -11d, -1d, -3d };
+ SummaryStatistics sampleStats1 = new SummaryStatistics();
+ for (int i = 0; i < sample1.length; i++) {
+ sampleStats1.addValue(sample1[i]);
+ }
+ SummaryStatistics sampleStats2 = new SummaryStatistics();
+ for (int i = 0; i < sample2.length; i++) {
+ sampleStats2.addValue(sample2[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("two sample heteroscedastic t stat", 1.60371728768,
+ TestUtils.t(sample1, sample2), 1E-10);
+ assertEquals("two sample heteroscedastic t stat", 1.60371728768,
+ TestUtils.t(sampleStats1, sampleStats2), 1E-10);
+ assertEquals("two sample heteroscedastic p value", 0.128839369622,
+ TestUtils.tTest(sample1, sample2), 1E-10);
+ assertEquals("two sample heteroscedastic p value", 0.128839369622,
+ TestUtils.tTest(sampleStats1, sampleStats2), 1E-10);
+ assertTrue("two sample heteroscedastic t-test reject",
+ TestUtils.tTest(sample1, sample2, 0.2));
+ assertTrue("two sample heteroscedastic t-test reject",
+ TestUtils.tTest(sampleStats1, sampleStats2, 0.2));
+ assertTrue("two sample heteroscedastic t-test accept",
+ !TestUtils.tTest(sample1, sample2, 0.1));
+ assertTrue("two sample heteroscedastic t-test accept",
+ !TestUtils.tTest(sampleStats1, sampleStats2, 0.1));
+
+ try {
+ TestUtils.tTest(sample1, sample2, .95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(sampleStats1, sampleStats2, .95);
+ fail("alpha out of range, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(sample1, tooShortObs, .01);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(sampleStats1, (SummaryStatistics) null, .01);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(sample1, tooShortObs);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.tTest(sampleStats1, (SummaryStatistics) null);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(sample1, tooShortObs);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ try {
+ TestUtils.t(sampleStats1, (SummaryStatistics) null);
+ fail("insufficient data, IllegalArgumentException expected");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+ public void testTwoSampleTHomoscedastic() throws Exception {
+ double[] sample1 ={2, 4, 6, 8, 10, 97};
+ double[] sample2 = {4, 6, 8, 10, 16};
+ SummaryStatistics sampleStats1 = new SummaryStatistics();
+ for (int i = 0; i < sample1.length; i++) {
+ sampleStats1.addValue(sample1[i]);
+ }
+ SummaryStatistics sampleStats2 = new SummaryStatistics();
+ for (int i = 0; i < sample2.length; i++) {
+ sampleStats2.addValue(sample2[i]);
+ }
+
+ // Target comparison values computed using R version 1.8.1 (Linux version)
+ assertEquals("two sample homoscedastic t stat", 0.73096310086,
+ TestUtils.homoscedasticT(sample1, sample2), 10E-11);
+ assertEquals("two sample homoscedastic p value", 0.4833963785,
+ TestUtils.homoscedasticTTest(sampleStats1, sampleStats2), 1E-10);
+ assertTrue("two sample homoscedastic t-test reject",
+ TestUtils.homoscedasticTTest(sample1, sample2, 0.49));
+ assertTrue("two sample homoscedastic t-test accept",
+ !TestUtils.homoscedasticTTest(sample1, sample2, 0.48));
+ }
+
+ public void testSmallSamples() throws Exception {
+ double[] sample1 = {1d, 3d};
+ double[] sample2 = {4d, 5d};
+
+ // Target values computed using R, version 1.8.1 (linux version)
+ assertEquals(-2.2360679775, TestUtils.t(sample1, sample2),
+ 1E-10);
+ assertEquals(0.198727388935, TestUtils.tTest(sample1, sample2),
+ 1E-10);
+ }
+
+ public void testPaired() throws Exception {
+ double[] sample1 = {1d, 3d, 5d, 7d};
+ double[] sample2 = {0d, 6d, 11d, 2d};
+ double[] sample3 = {5d, 7d, 8d, 10d};
+
+ // Target values computed using R, version 1.8.1 (linux version)
+ assertEquals(-0.3133, TestUtils.pairedT(sample1, sample2), 1E-4);
+ assertEquals(0.774544295819, TestUtils.pairedTTest(sample1, sample2), 1E-10);
+ assertEquals(0.001208, TestUtils.pairedTTest(sample1, sample3), 1E-6);
+ assertFalse(TestUtils.pairedTTest(sample1, sample3, .001));
+ assertTrue(TestUtils.pairedTTest(sample1, sample3, .002));
+ }
+
+ private double[] classA =
+ {93.0, 103.0, 95.0, 101.0};
+ private double[] classB =
+ {99.0, 92.0, 102.0, 100.0, 102.0};
+ private double[] classC =
+ {110.0, 115.0, 111.0, 117.0, 128.0};
+
+ private List<double[]> classes = new ArrayList<double[]>();
+ private OneWayAnova oneWayAnova = new OneWayAnovaImpl();
+
+ public void testOneWayAnovaUtils() throws Exception {
+ classes.add(classA);
+ classes.add(classB);
+ classes.add(classC);
+ assertEquals(oneWayAnova.anovaFValue(classes),
+ TestUtils.oneWayAnovaFValue(classes), 10E-12);
+ assertEquals(oneWayAnova.anovaPValue(classes),
+ TestUtils.oneWayAnovaPValue(classes), 10E-12);
+ assertEquals(oneWayAnova.anovaTest(classes, 0.01),
+ TestUtils.oneWayAnovaTest(classes, 0.01));
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/ranking/NaturalRankingTest.java b/src/test/java/org/apache/commons/math/stat/ranking/NaturalRankingTest.java
new file mode 100644
index 0000000..3d12bc5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/ranking/NaturalRankingTest.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.ranking;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.random.RandomGenerator;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for NaturalRanking class
+ *
+ * @since 2.0
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class NaturalRankingTest extends TestCase {
+
+ private final double[] exampleData = { 20, 17, 30, 42.3, 17, 50,
+ Double.NaN, Double.NEGATIVE_INFINITY, 17 };
+ private final double[] tiesFirst = { 0, 0, 2, 1, 4 };
+ private final double[] tiesLast = { 4, 4, 1, 0 };
+ private final double[] multipleNaNs = { 0, 1, Double.NaN, Double.NaN };
+ private final double[] multipleTies = { 3, 2, 5, 5, 6, 6, 1 };
+ private final double[] allSame = { 0, 0, 0, 0 };
+
+ public NaturalRankingTest(String arg0) {
+ super(arg0);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testDefault() { // Ties averaged, NaNs maximal
+ NaturalRanking ranking = new NaturalRanking();
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 5, 3, 6, 7, 3, 8, 9, 1, 3 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 1.5, 1.5, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 3.5, 3.5, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 1, 2, 3.5, 3.5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 4.5, 4.5, 6.5, 6.5, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 2.5, 2.5, 2.5, 2.5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsMaximalTiesMinimum() {
+ NaturalRanking ranking = new NaturalRanking(TiesStrategy.MINIMUM);
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 5, 2, 6, 7, 2, 8, 9, 1, 2 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 1, 1, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 3, 3, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 1, 2, 3, 3 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 4, 4, 6, 6, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 1, 1, 1, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsRemovedTiesSequential() {
+ NaturalRanking ranking = new NaturalRanking(NaNStrategy.REMOVED,
+ TiesStrategy.SEQUENTIAL);
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 5, 2, 6, 7, 3, 8, 1, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 1, 2, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 3, 4, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 1, 2 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 4, 5, 6, 7, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 1, 2, 3, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsMinimalTiesMaximum() {
+ NaturalRanking ranking = new NaturalRanking(NaNStrategy.MINIMAL,
+ TiesStrategy.MAXIMUM);
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 6, 5, 7, 8, 5, 9, 2, 2, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 2, 2, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 4, 4, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 3, 4, 2, 2 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 5, 5, 7, 7, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 4, 4, 4, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsMinimalTiesAverage() {
+ NaturalRanking ranking = new NaturalRanking(NaNStrategy.MINIMAL);
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 6, 4, 7, 8, 4, 9, 1.5, 1.5, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 1.5, 1.5, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 3.5, 3.5, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 3, 4, 1.5, 1.5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 4.5, 4.5, 6.5, 6.5, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 2.5, 2.5, 2.5, 2.5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsFixedTiesRandom() {
+ RandomGenerator randomGenerator = new JDKRandomGenerator();
+ randomGenerator.setSeed(1000);
+ NaturalRanking ranking = new NaturalRanking(NaNStrategy.FIXED,
+ randomGenerator);
+ double[] ranks = ranking.rank(exampleData);
+ double[] correctRanks = { 5, 4, 6, 7, 3, 8, Double.NaN, 1, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesFirst);
+ correctRanks = new double[] { 1, 1, 4, 3, 5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(tiesLast);
+ correctRanks = new double[] { 3, 4, 2, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleNaNs);
+ correctRanks = new double[] { 1, 2, Double.NaN, Double.NaN };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(multipleTies);
+ correctRanks = new double[] { 3, 2, 5, 5, 7, 6, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranks = ranking.rank(allSame);
+ correctRanks = new double[] { 1, 3, 4, 4 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+
+ public void testNaNsAndInfs() {
+ double[] data = { 0, Double.POSITIVE_INFINITY, Double.NaN,
+ Double.NEGATIVE_INFINITY };
+ NaturalRanking ranking = new NaturalRanking(NaNStrategy.MAXIMAL);
+ double[] ranks = ranking.rank(data);
+ double[] correctRanks = new double[] { 2, 3.5, 3.5, 1 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ ranking = new NaturalRanking(NaNStrategy.MINIMAL);
+ ranks = ranking.rank(data);
+ correctRanks = new double[] { 3, 4, 1.5, 1.5 };
+ TestUtils.assertEquals(correctRanks, ranks, 0d);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegressionTest.java b/src/test/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegressionTest.java
new file mode 100644
index 0000000..7b8f9fe
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/regression/GLSMultipleLinearRegressionTest.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.regression;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.apache.commons.math.TestUtils;
+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.random.CorrelatedRandomVectorGenerator;
+import org.apache.commons.math.random.JDKRandomGenerator;
+import org.apache.commons.math.random.GaussianRandomGenerator;
+import org.apache.commons.math.random.RandomGenerator;
+import org.apache.commons.math.stat.correlation.Covariance;
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+
+public class GLSMultipleLinearRegressionTest extends MultipleLinearRegressionAbstractTest {
+
+ private double[] y;
+ private double[][] x;
+ private double[][] omega;
+ private double[] longley = new double[] {
+ 60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962
+ };
+
+ @Before
+ @Override
+ public void setUp(){
+ y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+ x = new double[6][];
+ x[0] = new double[]{0, 0, 0, 0, 0};
+ x[1] = new double[]{2.0, 0, 0, 0, 0};
+ x[2] = new double[]{0, 3.0, 0, 0, 0};
+ x[3] = new double[]{0, 0, 4.0, 0, 0};
+ x[4] = new double[]{0, 0, 0, 5.0, 0};
+ x[5] = new double[]{0, 0, 0, 0, 6.0};
+ omega = new double[6][];
+ omega[0] = new double[]{1.0, 0, 0, 0, 0, 0};
+ omega[1] = new double[]{0, 2.0, 0, 0, 0, 0};
+ omega[2] = new double[]{0, 0, 3.0, 0, 0, 0};
+ omega[3] = new double[]{0, 0, 0, 4.0, 0, 0};
+ omega[4] = new double[]{0, 0, 0, 0, 5.0, 0};
+ omega[5] = new double[]{0, 0, 0, 0, 0, 6.0};
+ super.setUp();
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddXSampleData() {
+ createRegression().newSampleData(new double[]{}, null, null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddNullYSampleData() {
+ createRegression().newSampleData(null, new double[][]{}, null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddSampleDataWithSizeMismatch() {
+ double[] y = new double[]{1.0, 2.0};
+ double[][] x = new double[1][];
+ x[0] = new double[]{1.0, 0};
+ createRegression().newSampleData(y, x, null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddNullCovarianceData() {
+ createRegression().newSampleData(new double[]{}, new double[][]{}, null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void notEnoughData() {
+ double[] reducedY = new double[y.length - 1];
+ double[][] reducedX = new double[x.length - 1][];
+ double[][] reducedO = new double[omega.length - 1][];
+ System.arraycopy(y, 0, reducedY, 0, reducedY.length);
+ System.arraycopy(x, 0, reducedX, 0, reducedX.length);
+ System.arraycopy(omega, 0, reducedO, 0, reducedO.length);
+ createRegression().newSampleData(reducedY, reducedX, reducedO);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddCovarianceDataWithSampleSizeMismatch() {
+ double[] y = new double[]{1.0, 2.0};
+ double[][] x = new double[2][];
+ x[0] = new double[]{1.0, 0};
+ x[1] = new double[]{0, 1.0};
+ double[][] omega = new double[1][];
+ omega[0] = new double[]{1.0, 0};
+ createRegression().newSampleData(y, x, omega);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddCovarianceDataThatIsNotSquare() {
+ double[] y = new double[]{1.0, 2.0};
+ double[][] x = new double[2][];
+ x[0] = new double[]{1.0, 0};
+ x[1] = new double[]{0, 1.0};
+ double[][] omega = new double[3][];
+ omega[0] = new double[]{1.0, 0};
+ omega[1] = new double[]{0, 1.0};
+ omega[2] = new double[]{0, 2.0};
+ createRegression().newSampleData(y, x, omega);
+ }
+
+ @Override
+ protected GLSMultipleLinearRegression createRegression() {
+ GLSMultipleLinearRegression regression = new GLSMultipleLinearRegression();
+ regression.newSampleData(y, x, omega);
+ return regression;
+ }
+
+ @Override
+ protected int getNumberOfRegressors() {
+ return x[0].length + 1;
+ }
+
+ @Override
+ protected int getSampleSize() {
+ return y.length;
+ }
+
+ /**
+ * test calculateYVariance
+ */
+ @Test
+ public void testYVariance() {
+
+ // assumes: y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+
+ GLSMultipleLinearRegression model = new GLSMultipleLinearRegression();
+ model.newSampleData(y, x, omega);
+ TestUtils.assertEquals(model.calculateYVariance(), 3.5, 0);
+ }
+
+ /**
+ * Verifies that setting X, Y and covariance separately has the same effect as newSample(X,Y,cov).
+ */
+ @Test
+ public void testNewSample2() throws Exception {
+ double[] y = new double[] {1, 2, 3, 4};
+ double[][] x = new double[][] {
+ {19, 22, 33},
+ {20, 30, 40},
+ {25, 35, 45},
+ {27, 37, 47}
+ };
+ double[][] covariance = MatrixUtils.createRealIdentityMatrix(4).scalarMultiply(2).getData();
+ GLSMultipleLinearRegression regression = new GLSMultipleLinearRegression();
+ regression.newSampleData(y, x, covariance);
+ RealMatrix combinedX = regression.X.copy();
+ RealVector combinedY = regression.Y.copy();
+ RealMatrix combinedCovInv = regression.getOmegaInverse();
+ regression.newXSampleData(x);
+ regression.newYSampleData(y);
+ assertEquals(combinedX, regression.X);
+ assertEquals(combinedY, regression.Y);
+ assertEquals(combinedCovInv, regression.getOmegaInverse());
+ }
+
+ /**
+ * Verifies that GLS with identity covariance matrix gives the same results
+ * as OLS.
+ */
+ @Test
+ public void testGLSOLSConsistency() throws Exception {
+ RealMatrix identityCov = MatrixUtils.createRealIdentityMatrix(16);
+ GLSMultipleLinearRegression glsModel = new GLSMultipleLinearRegression();
+ OLSMultipleLinearRegression olsModel = new OLSMultipleLinearRegression();
+ glsModel.newSampleData(longley, 16, 6);
+ olsModel.newSampleData(longley, 16, 6);
+ glsModel.newCovarianceData(identityCov.getData());
+ double[] olsBeta = olsModel.calculateBeta().getData();
+ double[] glsBeta = glsModel.calculateBeta().getData();
+ // TODO: Should have assertRelativelyEquals(double[], double[], eps) in TestUtils
+ // Should also add RealVector and RealMatrix versions
+ for (int i = 0; i < olsBeta.length; i++) {
+ TestUtils.assertRelativelyEquals(olsBeta[i], glsBeta[i], 10E-7);
+ }
+ }
+
+ /**
+ * Generate an error covariance matrix and sample data representing models
+ * with this error structure. Then verify that GLS estimated coefficients,
+ * on average, perform better than OLS.
+ */
+ @Test
+ public void testGLSEfficiency() throws Exception {
+ RandomGenerator rg = new JDKRandomGenerator();
+ rg.setSeed(200); // Seed has been selected to generate non-trivial covariance
+
+ // Assume model has 16 observations (will use Longley data). Start by generating
+ // non-constant variances for the 16 error terms.
+ final int nObs = 16;
+ double[] sigma = new double[nObs];
+ for (int i = 0; i < nObs; i++) {
+ sigma[i] = 10 * rg.nextDouble();
+ }
+
+ // Now generate 1000 error vectors to use to estimate the covariance matrix
+ // Columns are draws on N(0, sigma[col])
+ final int numSeeds = 1000;
+ RealMatrix errorSeeds = MatrixUtils.createRealMatrix(numSeeds, nObs);
+ for (int i = 0; i < numSeeds; i++) {
+ for (int j = 0; j < nObs; j++) {
+ errorSeeds.setEntry(i, j, rg.nextGaussian() * sigma[j]);
+ }
+ }
+
+ // Get covariance matrix for columns
+ RealMatrix cov = (new Covariance(errorSeeds)).getCovarianceMatrix();
+
+ // Create a CorrelatedRandomVectorGenerator to use to generate correlated errors
+ GaussianRandomGenerator rawGenerator = new GaussianRandomGenerator(rg);
+ double[] errorMeans = new double[nObs]; // Counting on init to 0 here
+ CorrelatedRandomVectorGenerator gen = new CorrelatedRandomVectorGenerator(errorMeans, cov,
+ 1.0e-12 * cov.getNorm(), rawGenerator);
+
+ // Now start generating models. Use Longley X matrix on LHS
+ // and Longley OLS beta vector as "true" beta. Generate
+ // Y values by XB + u where u is a CorrelatedRandomVector generated
+ // from cov.
+ OLSMultipleLinearRegression ols = new OLSMultipleLinearRegression();
+ ols.newSampleData(longley, nObs, 6);
+ final RealVector b = ols.calculateBeta().copy();
+ final RealMatrix x = ols.X.copy();
+
+ // Create a GLS model to reuse
+ GLSMultipleLinearRegression gls = new GLSMultipleLinearRegression();
+ gls.newSampleData(longley, nObs, 6);
+ gls.newCovarianceData(cov.getData());
+
+ // Create aggregators for stats measuring model performance
+ DescriptiveStatistics olsBetaStats = new DescriptiveStatistics();
+ DescriptiveStatistics glsBetaStats = new DescriptiveStatistics();
+
+ // Generate Y vectors for 10000 models, estimate GLS and OLS and
+ // Verify that OLS estimates are better
+ final int nModels = 10000;
+ for (int i = 0; i < nModels; i++) {
+
+ // Generate y = xb + u with u cov
+ RealVector u = MatrixUtils.createRealVector(gen.nextVector());
+ double[] y = u.add(x.operate(b)).getData();
+
+ // Estimate OLS parameters
+ ols.newYSampleData(y);
+ RealVector olsBeta = ols.calculateBeta();
+
+ // Estimate GLS parameters
+ gls.newYSampleData(y);
+ RealVector glsBeta = gls.calculateBeta();
+
+ // Record deviations from "true" beta
+ double dist = olsBeta.getDistance(b);
+ olsBetaStats.addValue(dist * dist);
+ dist = glsBeta.getDistance(b);
+ glsBetaStats.addValue(dist * dist);
+
+ }
+
+ // Verify that GLS is on average more efficient, lower variance
+ assert(olsBetaStats.getMean() > 1.5 * glsBetaStats.getMean());
+ assert(olsBetaStats.getStandardDeviation() > glsBetaStats.getStandardDeviation());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/regression/MultipleLinearRegressionAbstractTest.java b/src/test/java/org/apache/commons/math/stat/regression/MultipleLinearRegressionAbstractTest.java
new file mode 100644
index 0000000..08801fd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/regression/MultipleLinearRegressionAbstractTest.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.RealMatrix;
+import org.apache.commons.math.linear.RealVector;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+public abstract class MultipleLinearRegressionAbstractTest {
+
+ protected AbstractMultipleLinearRegression regression;
+
+ @Before
+ public void setUp(){
+ regression = createRegression();
+ }
+
+ protected abstract AbstractMultipleLinearRegression createRegression();
+
+ protected abstract int getNumberOfRegressors();
+
+ protected abstract int getSampleSize();
+
+ @Test
+ public void canEstimateRegressionParameters(){
+ double[] beta = regression.estimateRegressionParameters();
+ assertEquals(getNumberOfRegressors(), beta.length);
+ }
+
+ @Test
+ public void canEstimateResiduals(){
+ double[] e = regression.estimateResiduals();
+ assertEquals(getSampleSize(), e.length);
+ }
+
+ @Test
+ public void canEstimateRegressionParametersVariance(){
+ double[][] variance = regression.estimateRegressionParametersVariance();
+ assertEquals(getNumberOfRegressors(), variance.length);
+ }
+
+ @Test
+ public void canEstimateRegressandVariance(){
+ if (getSampleSize() > getNumberOfRegressors()) {
+ double variance = regression.estimateRegressandVariance();
+ assertTrue(variance > 0.0);
+ }
+ }
+
+ /**
+ * Verifies that newSampleData methods consistently insert unitary columns
+ * in design matrix. Confirms the fix for MATH-411.
+ */
+ @Test
+ public void testNewSample() throws Exception {
+ double[] design = new double[] {
+ 1, 19, 22, 33,
+ 2, 20, 30, 40,
+ 3, 25, 35, 45,
+ 4, 27, 37, 47
+ };
+ double[] y = new double[] {1, 2, 3, 4};
+ double[][] x = new double[][] {
+ {19, 22, 33},
+ {20, 30, 40},
+ {25, 35, 45},
+ {27, 37, 47}
+ };
+ AbstractMultipleLinearRegression regression = createRegression();
+ regression.newSampleData(design, 4, 3);
+ RealMatrix flatX = regression.X.copy();
+ RealVector flatY = regression.Y.copy();
+ regression.newXSampleData(x);
+ regression.newYSampleData(y);
+ assertEquals(flatX, regression.X);
+ assertEquals(flatY, regression.Y);
+
+ // No intercept
+ regression.setNoIntercept(true);
+ regression.newSampleData(design, 4, 3);
+ flatX = regression.X.copy();
+ flatY = regression.Y.copy();
+ regression.newXSampleData(x);
+ regression.newYSampleData(y);
+ assertEquals(flatX, regression.X);
+ assertEquals(flatY, regression.Y);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNewSampleNullData() throws Exception {
+ double[] data = null;
+ createRegression().newSampleData(data, 2, 3);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNewSampleInvalidData() throws Exception {
+ double[] data = new double[] {1, 2, 3, 4};
+ createRegression().newSampleData(data, 2, 3);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNewSampleInsufficientData() throws Exception {
+ double[] data = new double[] {1, 2, 3, 4};
+ createRegression().newSampleData(data, 1, 3);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testXSampleDataNull() {
+ createRegression().newXSampleData(null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testYSampleDataNull() {
+ createRegression().newYSampleData(null);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegressionTest.java b/src/test/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegressionTest.java
new file mode 100644
index 0000000..52d9d65
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/regression/OLSMultipleLinearRegressionTest.java
@@ -0,0 +1,512 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 static org.junit.Assert.assertEquals;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.linear.DefaultRealMatrixChangingVisitor;
+import org.apache.commons.math.linear.MatrixUtils;
+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.stat.StatUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OLSMultipleLinearRegressionTest extends MultipleLinearRegressionAbstractTest {
+
+ private double[] y;
+ private double[][] x;
+
+ @Before
+ @Override
+ public void setUp(){
+ y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+ x = new double[6][];
+ x[0] = new double[]{0, 0, 0, 0, 0};
+ x[1] = new double[]{2.0, 0, 0, 0, 0};
+ x[2] = new double[]{0, 3.0, 0, 0, 0};
+ x[3] = new double[]{0, 0, 4.0, 0, 0};
+ x[4] = new double[]{0, 0, 0, 5.0, 0};
+ x[5] = new double[]{0, 0, 0, 0, 6.0};
+ super.setUp();
+ }
+
+ @Override
+ protected OLSMultipleLinearRegression createRegression() {
+ OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
+ regression.newSampleData(y, x);
+ return regression;
+ }
+
+ @Override
+ protected int getNumberOfRegressors() {
+ return x[0].length + 1;
+ }
+
+ @Override
+ protected int getSampleSize() {
+ return y.length;
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void cannotAddSampleDataWithSizeMismatch() {
+ double[] y = new double[]{1.0, 2.0};
+ double[][] x = new double[1][];
+ x[0] = new double[]{1.0, 0};
+ createRegression().newSampleData(y, x);
+ }
+
+ @Test
+ public void testPerfectFit() throws Exception {
+ double[] betaHat = regression.estimateRegressionParameters();
+ TestUtils.assertEquals(betaHat,
+ new double[]{ 11.0, 1.0 / 2.0, 2.0 / 3.0, 3.0 / 4.0, 4.0 / 5.0, 5.0 / 6.0 },
+ 1e-14);
+ double[] residuals = regression.estimateResiduals();
+ TestUtils.assertEquals(residuals, new double[]{0d,0d,0d,0d,0d,0d},
+ 1e-14);
+ RealMatrix errors =
+ new Array2DRowRealMatrix(regression.estimateRegressionParametersVariance(), false);
+ final double[] s = { 1.0, -1.0 / 2.0, -1.0 / 3.0, -1.0 / 4.0, -1.0 / 5.0, -1.0 / 6.0 };
+ RealMatrix referenceVariance = new Array2DRowRealMatrix(s.length, s.length);
+ referenceVariance.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
+ @Override
+ public double visit(int row, int column, double value) {
+ if (row == 0) {
+ return s[column];
+ }
+ double x = s[row] * s[column];
+ return (row == column) ? 2 * x : x;
+ }
+ });
+ assertEquals(0.0,
+ errors.subtract(referenceVariance).getNorm(),
+ 5.0e-16 * referenceVariance.getNorm());
+ assertEquals(1, ((OLSMultipleLinearRegression) regression).calculateRSquared(), 1E-12);
+ }
+
+
+ /**
+ * Test Longley dataset against certified values provided by NIST.
+ * Data Source: J. Longley (1967) "An Appraisal of Least Squares
+ * Programs for the Electronic Computer from the Point of View of the User"
+ * Journal of the American Statistical Association, vol. 62. September,
+ * pp. 819-841.
+ *
+ * Certified values (and data) are from NIST:
+ * http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Longley.dat
+ */
+ @Test
+ public void testLongly() throws Exception {
+ // Y values are first, then independent vars
+ // Each row is one observation
+ double[] design = new double[] {
+ 60323,83.0,234289,2356,1590,107608,1947,
+ 61122,88.5,259426,2325,1456,108632,1948,
+ 60171,88.2,258054,3682,1616,109773,1949,
+ 61187,89.5,284599,3351,1650,110929,1950,
+ 63221,96.2,328975,2099,3099,112075,1951,
+ 63639,98.1,346999,1932,3594,113270,1952,
+ 64989,99.0,365385,1870,3547,115094,1953,
+ 63761,100.0,363112,3578,3350,116219,1954,
+ 66019,101.2,397469,2904,3048,117388,1955,
+ 67857,104.6,419180,2822,2857,118734,1956,
+ 68169,108.4,442769,2936,2798,120445,1957,
+ 66513,110.8,444546,4681,2637,121950,1958,
+ 68655,112.6,482704,3813,2552,123366,1959,
+ 69564,114.2,502601,3931,2514,125368,1960,
+ 69331,115.7,518173,4806,2572,127852,1961,
+ 70551,116.9,554894,4007,2827,130081,1962
+ };
+
+ final int nobs = 16;
+ final int nvars = 6;
+
+ // Estimate the model
+ OLSMultipleLinearRegression model = new OLSMultipleLinearRegression();
+ model.newSampleData(design, nobs, nvars);
+
+ // Check expected beta values from NIST
+ double[] betaHat = model.estimateRegressionParameters();
+ TestUtils.assertEquals(betaHat,
+ new double[]{-3482258.63459582, 15.0618722713733,
+ -0.358191792925910E-01,-2.02022980381683,
+ -1.03322686717359,-0.511041056535807E-01,
+ 1829.15146461355}, 2E-8); //
+
+ // Check expected residuals from R
+ double[] residuals = model.estimateResiduals();
+ TestUtils.assertEquals(residuals, new double[]{
+ 267.340029759711,-94.0139423988359,46.28716775752924,
+ -410.114621930906,309.7145907602313,-249.3112153297231,
+ -164.0489563956039,-13.18035686637081,14.30477260005235,
+ 455.394094551857,-17.26892711483297,-39.0550425226967,
+ -155.5499735953195,-85.6713080421283,341.9315139607727,
+ -206.7578251937366},
+ 1E-8);
+
+ // Check standard errors from NIST
+ double[] errors = model.estimateRegressionParametersStandardErrors();
+ TestUtils.assertEquals(new double[] {890420.383607373,
+ 84.9149257747669,
+ 0.334910077722432E-01,
+ 0.488399681651699,
+ 0.214274163161675,
+ 0.226073200069370,
+ 455.478499142212}, errors, 1E-6);
+
+ // Check regression standard error against R
+ assertEquals(304.8540735619638, model.estimateRegressionStandardError(), 1E-10);
+
+ // Check R-Square statistics against R
+ assertEquals(0.995479004577296, model.calculateRSquared(), 1E-12);
+ assertEquals(0.992465007628826, model.calculateAdjustedRSquared(), 1E-12);
+
+ checkVarianceConsistency(model);
+
+ // Estimate model without intercept
+ model.setNoIntercept(true);
+ model.newSampleData(design, nobs, nvars);
+
+ // Check expected beta values from R
+ betaHat = model.estimateRegressionParameters();
+ TestUtils.assertEquals(betaHat,
+ new double[]{-52.99357013868291, 0.07107319907358,
+ -0.42346585566399,-0.57256866841929,
+ -0.41420358884978, 48.41786562001326}, 1E-11);
+
+ // Check standard errors from R
+ errors = model.estimateRegressionParametersStandardErrors();
+ TestUtils.assertEquals(new double[] {129.54486693117232, 0.03016640003786,
+ 0.41773654056612, 0.27899087467676, 0.32128496193363,
+ 17.68948737819961}, errors, 1E-11);
+
+ // Check expected residuals from R
+ residuals = model.estimateResiduals();
+ TestUtils.assertEquals(residuals, new double[]{
+ 279.90274927293092, -130.32465380836874, 90.73228661967445, -401.31252201634948,
+ -440.46768772620027, -543.54512853774793, 201.32111639536299, 215.90889365977932,
+ 73.09368242049943, 913.21694494481869, 424.82484953610174, -8.56475876776709,
+ -361.32974610842876, 27.34560497213464, 151.28955976355002, -492.49937355336846},
+ 1E-10);
+
+ // Check regression standard error against R
+ assertEquals(475.1655079819517, model.estimateRegressionStandardError(), 1E-10);
+
+ // Check R-Square statistics against R
+ assertEquals(0.9999670130706, model.calculateRSquared(), 1E-12);
+ assertEquals(0.999947220913, model.calculateAdjustedRSquared(), 1E-12);
+
+ }
+
+ /**
+ * Test R Swiss fertility dataset against R.
+ * Data Source: R datasets package
+ */
+ @Test
+ public void testSwissFertility() throws Exception {
+ double[] design = new double[] {
+ 80.2,17.0,15,12,9.96,
+ 83.1,45.1,6,9,84.84,
+ 92.5,39.7,5,5,93.40,
+ 85.8,36.5,12,7,33.77,
+ 76.9,43.5,17,15,5.16,
+ 76.1,35.3,9,7,90.57,
+ 83.8,70.2,16,7,92.85,
+ 92.4,67.8,14,8,97.16,
+ 82.4,53.3,12,7,97.67,
+ 82.9,45.2,16,13,91.38,
+ 87.1,64.5,14,6,98.61,
+ 64.1,62.0,21,12,8.52,
+ 66.9,67.5,14,7,2.27,
+ 68.9,60.7,19,12,4.43,
+ 61.7,69.3,22,5,2.82,
+ 68.3,72.6,18,2,24.20,
+ 71.7,34.0,17,8,3.30,
+ 55.7,19.4,26,28,12.11,
+ 54.3,15.2,31,20,2.15,
+ 65.1,73.0,19,9,2.84,
+ 65.5,59.8,22,10,5.23,
+ 65.0,55.1,14,3,4.52,
+ 56.6,50.9,22,12,15.14,
+ 57.4,54.1,20,6,4.20,
+ 72.5,71.2,12,1,2.40,
+ 74.2,58.1,14,8,5.23,
+ 72.0,63.5,6,3,2.56,
+ 60.5,60.8,16,10,7.72,
+ 58.3,26.8,25,19,18.46,
+ 65.4,49.5,15,8,6.10,
+ 75.5,85.9,3,2,99.71,
+ 69.3,84.9,7,6,99.68,
+ 77.3,89.7,5,2,100.00,
+ 70.5,78.2,12,6,98.96,
+ 79.4,64.9,7,3,98.22,
+ 65.0,75.9,9,9,99.06,
+ 92.2,84.6,3,3,99.46,
+ 79.3,63.1,13,13,96.83,
+ 70.4,38.4,26,12,5.62,
+ 65.7,7.7,29,11,13.79,
+ 72.7,16.7,22,13,11.22,
+ 64.4,17.6,35,32,16.92,
+ 77.6,37.6,15,7,4.97,
+ 67.6,18.7,25,7,8.65,
+ 35.0,1.2,37,53,42.34,
+ 44.7,46.6,16,29,50.43,
+ 42.8,27.7,22,29,58.33
+ };
+
+ final int nobs = 47;
+ final int nvars = 4;
+
+ // Estimate the model
+ OLSMultipleLinearRegression model = new OLSMultipleLinearRegression();
+ model.newSampleData(design, nobs, nvars);
+
+ // Check expected beta values from R
+ double[] betaHat = model.estimateRegressionParameters();
+ TestUtils.assertEquals(betaHat,
+ new double[]{91.05542390271397,
+ -0.22064551045715,
+ -0.26058239824328,
+ -0.96161238456030,
+ 0.12441843147162}, 1E-12);
+
+ // Check expected residuals from R
+ double[] residuals = model.estimateResiduals();
+ TestUtils.assertEquals(residuals, new double[]{
+ 7.1044267859730512,1.6580347433531366,
+ 4.6944952770029644,8.4548022690166160,13.6547432343186212,
+ -9.3586864458500774,7.5822446330520386,15.5568995563859289,
+ 0.8113090736598980,7.1186762732484308,7.4251378771228724,
+ 2.6761316873234109,0.8351584810309354,7.1769991119615177,
+ -3.8746753206299553,-3.1337779476387251,-0.1412575244091504,
+ 1.1186809170469780,-6.3588097346816594,3.4039270429434074,
+ 2.3374058329820175,-7.9272368576900503,-7.8361010968497959,
+ -11.2597369269357070,0.9445333697827101,6.6544245101380328,
+ -0.9146136301118665,-4.3152449403848570,-4.3536932047009183,
+ -3.8907885169304661,-6.3027643926302188,-7.8308982189289091,
+ -3.1792280015332750,-6.7167298771158226,-4.8469946718041754,
+ -10.6335664353633685,11.1031134362036958,6.0084032641811733,
+ 5.4326230830188482,-7.2375578629692230,2.1671550814448222,
+ 15.0147574652763112,4.8625103516321015,-7.1597256413907706,
+ -0.4515205619767598,-10.2916870903837587,-15.7812984571900063},
+ 1E-12);
+
+ // Check standard errors from R
+ double[] errors = model.estimateRegressionParametersStandardErrors();
+ TestUtils.assertEquals(new double[] {6.94881329475087,
+ 0.07360008972340,
+ 0.27410957467466,
+ 0.19454551679325,
+ 0.03726654773803}, errors, 1E-10);
+
+ // Check regression standard error against R
+ assertEquals(7.73642194433223, model.estimateRegressionStandardError(), 1E-12);
+
+ // Check R-Square statistics against R
+ assertEquals(0.649789742860228, model.calculateRSquared(), 1E-12);
+ assertEquals(0.6164363850373927, model.calculateAdjustedRSquared(), 1E-12);
+
+ checkVarianceConsistency(model);
+
+ // Estimate the model with no intercept
+ model = new OLSMultipleLinearRegression();
+ model.setNoIntercept(true);
+ model.newSampleData(design, nobs, nvars);
+
+ // Check expected beta values from R
+ betaHat = model.estimateRegressionParameters();
+ TestUtils.assertEquals(betaHat,
+ new double[]{0.52191832900513,
+ 2.36588087917963,
+ -0.94770353802795,
+ 0.30851985863609}, 1E-12);
+
+ // Check expected residuals from R
+ residuals = model.estimateResiduals();
+ TestUtils.assertEquals(residuals, new double[]{
+ 44.138759883538249, 27.720705122356215, 35.873200836126799,
+ 34.574619581211977, 26.600168342080213, 15.074636243026923, -12.704904871199814,
+ 1.497443824078134, 2.691972687079431, 5.582798774291231, -4.422986561283165,
+ -9.198581600334345, 4.481765170730647, 2.273520207553216, -22.649827853221336,
+ -17.747900013943308, 20.298314638496436, 6.861405135329779, -8.684712790954924,
+ -10.298639278062371, -9.896618896845819, 4.568568616351242, -15.313570491727944,
+ -13.762961360873966, 7.156100301980509, 16.722282219843990, 26.716200609071898,
+ -1.991466398777079, -2.523342564719335, 9.776486693095093, -5.297535127628603,
+ -16.639070567471094, -10.302057295211819, -23.549487860816846, 1.506624392156384,
+ -17.939174438345930, 13.105792202765040, -1.943329906928462, -1.516005841666695,
+ -0.759066561832886, 20.793137744128977, -2.485236153005426, 27.588238710486976,
+ 2.658333257106881, -15.998337823623046, -5.550742066720694, -14.219077806826615},
+ 1E-12);
+
+ // Check standard errors from R
+ errors = model.estimateRegressionParametersStandardErrors();
+ TestUtils.assertEquals(new double[] {0.10470063765677, 0.41684100584290,
+ 0.43370143099691, 0.07694953606522}, errors, 1E-10);
+
+ // Check regression standard error against R
+ assertEquals(17.24710630547, model.estimateRegressionStandardError(), 1E-10);
+
+ // Check R-Square statistics against R
+ assertEquals(0.946350722085, model.calculateRSquared(), 1E-12);
+ assertEquals(0.9413600915813, model.calculateAdjustedRSquared(), 1E-12);
+ }
+
+ /**
+ * Test hat matrix computation
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testHat() throws Exception {
+
+ /*
+ * This example is from "The Hat Matrix in Regression and ANOVA",
+ * David C. Hoaglin and Roy E. Welsch,
+ * The American Statistician, Vol. 32, No. 1 (Feb., 1978), pp. 17-22.
+ *
+ */
+ double[] design = new double[] {
+ 11.14, .499, 11.1,
+ 12.74, .558, 8.9,
+ 13.13, .604, 8.8,
+ 11.51, .441, 8.9,
+ 12.38, .550, 8.8,
+ 12.60, .528, 9.9,
+ 11.13, .418, 10.7,
+ 11.7, .480, 10.5,
+ 11.02, .406, 10.5,
+ 11.41, .467, 10.7
+ };
+
+ int nobs = 10;
+ int nvars = 2;
+
+ // Estimate the model
+ OLSMultipleLinearRegression model = new OLSMultipleLinearRegression();
+ model.newSampleData(design, nobs, nvars);
+
+ RealMatrix hat = model.calculateHat();
+
+ // Reference data is upper half of symmetric hat matrix
+ double[] referenceData = new double[] {
+ .418, -.002, .079, -.274, -.046, .181, .128, .222, .050, .242,
+ .242, .292, .136, .243, .128, -.041, .033, -.035, .004,
+ .417, -.019, .273, .187, -.126, .044, -.153, .004,
+ .604, .197, -.038, .168, -.022, .275, -.028,
+ .252, .111, -.030, .019, -.010, -.010,
+ .148, .042, .117, .012, .111,
+ .262, .145, .277, .174,
+ .154, .120, .168,
+ .315, .148,
+ .187
+ };
+
+ // Check against reference data and verify symmetry
+ int k = 0;
+ for (int i = 0; i < 10; i++) {
+ for (int j = i; j < 10; j++) {
+ assertEquals(referenceData[k], hat.getEntry(i, j), 10e-3);
+ assertEquals(hat.getEntry(i, j), hat.getEntry(j, i), 10e-12);
+ k++;
+ }
+ }
+
+ /*
+ * Verify that residuals computed using the hat matrix are close to
+ * what we get from direct computation, i.e. r = (I - H) y
+ */
+ double[] residuals = model.estimateResiduals();
+ RealMatrix I = MatrixUtils.createRealIdentityMatrix(10);
+ double[] hatResiduals = I.subtract(hat).operate(model.Y).getData();
+ TestUtils.assertEquals(residuals, hatResiduals, 10e-12);
+ }
+
+ /**
+ * test calculateYVariance
+ */
+ @Test
+ public void testYVariance() {
+
+ // assumes: y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
+
+ OLSMultipleLinearRegression model = new OLSMultipleLinearRegression();
+ model.newSampleData(y, x);
+ TestUtils.assertEquals(model.calculateYVariance(), 3.5, 0);
+ }
+
+ /**
+ * Verifies that calculateYVariance and calculateResidualVariance return consistent
+ * values with direct variance computation from Y, residuals, respectively.
+ */
+ protected void checkVarianceConsistency(OLSMultipleLinearRegression model) throws Exception {
+ // Check Y variance consistency
+ TestUtils.assertEquals(StatUtils.variance(model.Y.getData()), model.calculateYVariance(), 0);
+
+ // Check residual variance consistency
+ double[] residuals = model.calculateResiduals().getData();
+ RealMatrix X = model.X;
+ TestUtils.assertEquals(
+ StatUtils.variance(model.calculateResiduals().getData()) * (residuals.length - 1),
+ model.calculateErrorVariance() * (X.getRowDimension() - X.getColumnDimension()), 1E-20);
+
+ }
+
+ /**
+ * Verifies that setting X and Y separately has the same effect as newSample(X,Y).
+ */
+ @Test
+ public void testNewSample2() throws Exception {
+ double[] y = new double[] {1, 2, 3, 4};
+ double[][] x = new double[][] {
+ {19, 22, 33},
+ {20, 30, 40},
+ {25, 35, 45},
+ {27, 37, 47}
+ };
+ OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
+ regression.newSampleData(y, x);
+ RealMatrix combinedX = regression.X.copy();
+ RealVector combinedY = regression.Y.copy();
+ regression.newXSampleData(x);
+ regression.newYSampleData(y);
+ assertEquals(combinedX, regression.X);
+ assertEquals(combinedY, regression.Y);
+
+ // No intercept
+ regression.setNoIntercept(true);
+ regression.newSampleData(y, x);
+ combinedX = regression.X.copy();
+ combinedY = regression.Y.copy();
+ regression.newXSampleData(x);
+ regression.newYSampleData(y);
+ assertEquals(combinedX, regression.X);
+ assertEquals(combinedY, regression.Y);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNewSampleDataYNull() {
+ createRegression().newSampleData(null, new double[][] {});
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNewSampleDataXNull() {
+ createRegression().newSampleData(new double[] {}, null);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/stat/regression/SimpleRegressionTest.java b/src/test/java/org/apache/commons/math/stat/regression/SimpleRegressionTest.java
new file mode 100644
index 0000000..c71a877
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/stat/regression/SimpleRegressionTest.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.stat.regression;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the TestStatistic class.
+ *
+ * @version $Revision: 902201 $ $Date: 2010-01-22 19:18:16 +0100 (ven. 22 janv. 2010) $
+ */
+
+public final class SimpleRegressionTest extends TestCase {
+
+ /*
+ * NIST "Norris" refernce data set from
+ * http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Norris.dat
+ * Strangely, order is {y,x}
+ */
+ private double[][] data = { { 0.1, 0.2 }, {338.8, 337.4 }, {118.1, 118.2 },
+ {888.0, 884.6 }, {9.2, 10.1 }, {228.1, 226.5 }, {668.5, 666.3 }, {998.5, 996.3 },
+ {449.1, 448.6 }, {778.9, 777.0 }, {559.2, 558.2 }, {0.3, 0.4 }, {0.1, 0.6 }, {778.1, 775.5 },
+ {668.8, 666.9 }, {339.3, 338.0 }, {448.9, 447.5 }, {10.8, 11.6 }, {557.7, 556.0 },
+ {228.3, 228.1 }, {998.0, 995.8 }, {888.8, 887.6 }, {119.6, 120.2 }, {0.3, 0.3 },
+ {0.6, 0.3 }, {557.6, 556.8 }, {339.3, 339.1 }, {888.0, 887.2 }, {998.5, 999.0 },
+ {778.9, 779.0 }, {10.2, 11.1 }, {117.6, 118.3 }, {228.9, 229.2 }, {668.4, 669.1 },
+ {449.2, 448.9 }, {0.2, 0.5 }
+ };
+
+ /*
+ * Correlation example from
+ * http://www.xycoon.com/correlation.htm
+ */
+ private double[][] corrData = { { 101.0, 99.2 }, {100.1, 99.0 }, {100.0, 100.0 },
+ {90.6, 111.6 }, {86.5, 122.2 }, {89.7, 117.6 }, {90.6, 121.1 }, {82.8, 136.0 },
+ {70.1, 154.2 }, {65.4, 153.6 }, {61.3, 158.5 }, {62.5, 140.6 }, {63.6, 136.2 },
+ {52.6, 168.0 }, {59.7, 154.3 }, {59.5, 149.0 }, {61.3, 165.5 }
+ };
+
+ /*
+ * From Moore and Mcabe, "Introduction to the Practice of Statistics"
+ * Example 10.3
+ */
+ private double[][] infData = { { 15.6, 5.2 }, {26.8, 6.1 }, {37.8, 8.7 }, {36.4, 8.5 },
+ {35.5, 8.8 }, {18.6, 4.9 }, {15.3, 4.5 }, {7.9, 2.5 }, {0.0, 1.1 }
+ };
+
+ /*
+ * Points to remove in the remove tests
+ */
+ private double[][] removeSingle = {infData[1]};
+ private double[][] removeMultiple = { infData[1], infData[2] };
+ private double removeX = infData[0][0];
+ private double removeY = infData[0][1];
+
+
+ /*
+ * Data with bad linear fit
+ */
+ private double[][] infData2 = { { 1, 1 }, {2, 0 }, {3, 5 }, {4, 2 },
+ {5, -1 }, {6, 12 }
+ };
+
+ public SimpleRegressionTest(String name) {
+ super(name);
+ }
+
+ public void testNorris() {
+ SimpleRegression regression = new SimpleRegression();
+ for (int i = 0; i < data.length; i++) {
+ regression.addData(data[i][1], data[i][0]);
+ }
+ // Tests against certified values from
+ // http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Norris.dat
+ assertEquals("slope", 1.00211681802045, regression.getSlope(), 10E-12);
+ assertEquals("slope std err", 0.429796848199937E-03,
+ regression.getSlopeStdErr(),10E-12);
+ assertEquals("number of observations", 36, regression.getN());
+ assertEquals( "intercept", -0.262323073774029,
+ regression.getIntercept(),10E-12);
+ assertEquals("std err intercept", 0.232818234301152,
+ regression.getInterceptStdErr(),10E-12);
+ assertEquals("r-square", 0.999993745883712,
+ regression.getRSquare(), 10E-12);
+ assertEquals("SSR", 4255954.13232369,
+ regression.getRegressionSumSquares(), 10E-9);
+ assertEquals("MSE", 0.782864662630069,
+ regression.getMeanSquareError(), 10E-10);
+ assertEquals("SSE", 26.6173985294224,
+ regression.getSumSquaredErrors(),10E-9);
+ // ------------ End certified data tests
+
+ assertEquals( "predict(0)", -0.262323073774029,
+ regression.predict(0), 10E-12);
+ assertEquals("predict(1)", 1.00211681802045 - 0.262323073774029,
+ regression.predict(1), 10E-12);
+ }
+
+ public void testCorr() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(corrData);
+ assertEquals("number of observations", 17, regression.getN());
+ assertEquals("r-square", .896123, regression.getRSquare(), 10E-6);
+ assertEquals("r", -0.94663767742, regression.getR(), 1E-10);
+ }
+
+ public void testNaNs() {
+ SimpleRegression regression = new SimpleRegression();
+ assertTrue("intercept not NaN", Double.isNaN(regression.getIntercept()));
+ assertTrue("slope not NaN", Double.isNaN(regression.getSlope()));
+ assertTrue("slope std err not NaN", Double.isNaN(regression.getSlopeStdErr()));
+ assertTrue("intercept std err not NaN", Double.isNaN(regression.getInterceptStdErr()));
+ assertTrue("MSE not NaN", Double.isNaN(regression.getMeanSquareError()));
+ assertTrue("e not NaN", Double.isNaN(regression.getR()));
+ assertTrue("r-square not NaN", Double.isNaN(regression.getRSquare()));
+ assertTrue( "RSS not NaN", Double.isNaN(regression.getRegressionSumSquares()));
+ assertTrue("SSE not NaN",Double.isNaN(regression.getSumSquaredErrors()));
+ assertTrue("SSTO not NaN", Double.isNaN(regression.getTotalSumSquares()));
+ assertTrue("predict not NaN", Double.isNaN(regression.predict(0)));
+
+ regression.addData(1, 2);
+ regression.addData(1, 3);
+
+ // No x variation, so these should still blow...
+ assertTrue("intercept not NaN", Double.isNaN(regression.getIntercept()));
+ assertTrue("slope not NaN", Double.isNaN(regression.getSlope()));
+ assertTrue("slope std err not NaN", Double.isNaN(regression.getSlopeStdErr()));
+ assertTrue("intercept std err not NaN", Double.isNaN(regression.getInterceptStdErr()));
+ assertTrue("MSE not NaN", Double.isNaN(regression.getMeanSquareError()));
+ assertTrue("e not NaN", Double.isNaN(regression.getR()));
+ assertTrue("r-square not NaN", Double.isNaN(regression.getRSquare()));
+ assertTrue("RSS not NaN", Double.isNaN(regression.getRegressionSumSquares()));
+ assertTrue("SSE not NaN", Double.isNaN(regression.getSumSquaredErrors()));
+ assertTrue("predict not NaN", Double.isNaN(regression.predict(0)));
+
+ // but SSTO should be OK
+ assertTrue("SSTO NaN", !Double.isNaN(regression.getTotalSumSquares()));
+
+ regression = new SimpleRegression();
+
+ regression.addData(1, 2);
+ regression.addData(3, 3);
+
+ // All should be OK except MSE, s(b0), s(b1) which need one more df
+ assertTrue("interceptNaN", !Double.isNaN(regression.getIntercept()));
+ assertTrue("slope NaN", !Double.isNaN(regression.getSlope()));
+ assertTrue ("slope std err not NaN", Double.isNaN(regression.getSlopeStdErr()));
+ assertTrue("intercept std err not NaN", Double.isNaN(regression.getInterceptStdErr()));
+ assertTrue("MSE not NaN", Double.isNaN(regression.getMeanSquareError()));
+ assertTrue("r NaN", !Double.isNaN(regression.getR()));
+ assertTrue("r-square NaN", !Double.isNaN(regression.getRSquare()));
+ assertTrue("RSS NaN", !Double.isNaN(regression.getRegressionSumSquares()));
+ assertTrue("SSE NaN", !Double.isNaN(regression.getSumSquaredErrors()));
+ assertTrue("SSTO NaN", !Double.isNaN(regression.getTotalSumSquares()));
+ assertTrue("predict NaN", !Double.isNaN(regression.predict(0)));
+
+ regression.addData(1, 4);
+
+ // MSE, MSE, s(b0), s(b1) should all be OK now
+ assertTrue("MSE NaN", !Double.isNaN(regression.getMeanSquareError()));
+ assertTrue("slope std err NaN", !Double.isNaN(regression.getSlopeStdErr()));
+ assertTrue("intercept std err NaN", !Double.isNaN(regression.getInterceptStdErr()));
+ }
+
+ public void testClear() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(corrData);
+ assertEquals("number of observations", 17, regression.getN());
+ regression.clear();
+ assertEquals("number of observations", 0, regression.getN());
+ regression.addData(corrData);
+ assertEquals("r-square", .896123, regression.getRSquare(), 10E-6);
+ regression.addData(data);
+ assertEquals("number of observations", 53, regression.getN());
+ }
+
+ public void testInference() throws Exception {
+ //---------- verified against R, version 1.8.1 -----
+ // infData
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(infData);
+ assertEquals("slope std err", 0.011448491,
+ regression.getSlopeStdErr(), 1E-10);
+ assertEquals("std err intercept", 0.286036932,
+ regression.getInterceptStdErr(),1E-8);
+ assertEquals("significance", 4.596e-07,
+ regression.getSignificance(),1E-8);
+ assertEquals("slope conf interval half-width", 0.0270713794287,
+ regression.getSlopeConfidenceInterval(),1E-8);
+ // infData2
+ regression = new SimpleRegression();
+ regression.addData(infData2);
+ assertEquals("slope std err", 1.07260253,
+ regression.getSlopeStdErr(), 1E-8);
+ assertEquals("std err intercept",4.17718672,
+ regression.getInterceptStdErr(),1E-8);
+ assertEquals("significance", 0.261829133982,
+ regression.getSignificance(),1E-11);
+ assertEquals("slope conf interval half-width", 2.97802204827,
+ regression.getSlopeConfidenceInterval(),1E-8);
+ //------------- End R-verified tests -------------------------------
+
+ //FIXME: get a real example to test against with alpha = .01
+ assertTrue("tighter means wider",
+ regression.getSlopeConfidenceInterval() < regression.getSlopeConfidenceInterval(0.01));
+
+ try {
+ regression.getSlopeConfidenceInterval(1);
+ fail("expecting IllegalArgumentException for alpha = 1");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ }
+
+ public void testPerfect() throws Exception {
+ SimpleRegression regression = new SimpleRegression();
+ int n = 100;
+ for (int i = 0; i < n; i++) {
+ regression.addData(((double) i) / (n - 1), i);
+ }
+ assertEquals(0.0, regression.getSignificance(), 1.0e-5);
+ assertTrue(regression.getSlope() > 0.0);
+ assertTrue(regression.getSumSquaredErrors() >= 0.0);
+ }
+
+ public void testPerfectNegative() throws Exception {
+ SimpleRegression regression = new SimpleRegression();
+ int n = 100;
+ for (int i = 0; i < n; i++) {
+ regression.addData(- ((double) i) / (n - 1), i);
+ }
+
+ assertEquals(0.0, regression.getSignificance(), 1.0e-5);
+ assertTrue(regression.getSlope() < 0.0);
+ }
+
+ public void testRandom() throws Exception {
+ SimpleRegression regression = new SimpleRegression();
+ Random random = new Random(1);
+ int n = 100;
+ for (int i = 0; i < n; i++) {
+ regression.addData(((double) i) / (n - 1), random.nextDouble());
+ }
+
+ assertTrue( 0.0 < regression.getSignificance()
+ && regression.getSignificance() < 1.0);
+ }
+
+
+ // Jira MATH-85 = Bugzilla 39432
+ public void testSSENonNegative() {
+ double[] y = { 8915.102, 8919.302, 8923.502 };
+ double[] x = { 1.107178495E2, 1.107264895E2, 1.107351295E2 };
+ SimpleRegression reg = new SimpleRegression();
+ for (int i = 0; i < x.length; i++) {
+ reg.addData(x[i], y[i]);
+ }
+ assertTrue(reg.getSumSquaredErrors() >= 0.0);
+ }
+
+ // Test remove X,Y (single observation)
+ public void testRemoveXY() throws Exception {
+ // Create regression with inference data then remove to test
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(infData);
+ regression.removeData(removeX, removeY);
+ regression.addData(removeX, removeY);
+ // Use the inference assertions to make sure that everything worked
+ assertEquals("slope std err", 0.011448491,
+ regression.getSlopeStdErr(), 1E-10);
+ assertEquals("std err intercept", 0.286036932,
+ regression.getInterceptStdErr(),1E-8);
+ assertEquals("significance", 4.596e-07,
+ regression.getSignificance(),1E-8);
+ assertEquals("slope conf interval half-width", 0.0270713794287,
+ regression.getSlopeConfidenceInterval(),1E-8);
+ }
+
+
+ // Test remove single observation in array
+ public void testRemoveSingle() throws Exception {
+ // Create regression with inference data then remove to test
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(infData);
+ regression.removeData(removeSingle);
+ regression.addData(removeSingle);
+ // Use the inference assertions to make sure that everything worked
+ assertEquals("slope std err", 0.011448491,
+ regression.getSlopeStdErr(), 1E-10);
+ assertEquals("std err intercept", 0.286036932,
+ regression.getInterceptStdErr(),1E-8);
+ assertEquals("significance", 4.596e-07,
+ regression.getSignificance(),1E-8);
+ assertEquals("slope conf interval half-width", 0.0270713794287,
+ regression.getSlopeConfidenceInterval(),1E-8);
+ }
+
+ // Test remove multiple observations
+ public void testRemoveMultiple() throws Exception {
+ // Create regression with inference data then remove to test
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(infData);
+ regression.removeData(removeMultiple);
+ regression.addData(removeMultiple);
+ // Use the inference assertions to make sure that everything worked
+ assertEquals("slope std err", 0.011448491,
+ regression.getSlopeStdErr(), 1E-10);
+ assertEquals("std err intercept", 0.286036932,
+ regression.getInterceptStdErr(),1E-8);
+ assertEquals("significance", 4.596e-07,
+ regression.getSignificance(),1E-8);
+ assertEquals("slope conf interval half-width", 0.0270713794287,
+ regression.getSlopeConfidenceInterval(),1E-8);
+ }
+
+ // Remove observation when empty
+ public void testRemoveObsFromEmpty() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.removeData(removeX, removeY);
+ assertEquals(regression.getN(), 0);
+ }
+
+ // Remove single observation to empty
+ public void testRemoveObsFromSingle() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(removeX, removeY);
+ regression.removeData(removeX, removeY);
+ assertEquals(regression.getN(), 0);
+ }
+
+ // Remove multiple observations to empty
+ public void testRemoveMultipleToEmpty() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(removeMultiple);
+ regression.removeData(removeMultiple);
+ assertEquals(regression.getN(), 0);
+ }
+
+ // Remove multiple observations past empty (i.e. size of array > n)
+ public void testRemoveMultiplePastEmpty() {
+ SimpleRegression regression = new SimpleRegression();
+ regression.addData(removeX, removeY);
+ regression.removeData(removeMultiple);
+ assertEquals(regression.getN(), 0);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/transform/FastCosineTransformerTest.java b/src/test/java/org/apache/commons/math/transform/FastCosineTransformerTest.java
new file mode 100644
index 0000000..cb1edf4
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/transform/FastCosineTransformerTest.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.transform;
+
+import org.apache.commons.math.analysis.*;
+import org.apache.commons.math.util.FastMath;
+import junit.framework.TestCase;
+
+/**
+ * Testcase for fast cosine transformer.
+ * <p>
+ * FCT algorithm is exact, the small tolerance number is used only
+ * to account for round-off errors.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public final class FastCosineTransformerTest extends TestCase {
+
+ /**
+ * Test of transformer for the ad hoc data.
+ */
+ public void testAdHocData() {
+ FastCosineTransformer transformer = new FastCosineTransformer();
+ double result[], tolerance = 1E-12;
+
+ double x[] = { 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0 };
+ double y[] = { 172.0, -105.096569476353, 27.3137084989848,
+ -12.9593152353742, 8.0, -5.78585076868676,
+ 4.68629150101524, -4.15826451958632, 4.0 };
+
+ result = transformer.transform(x);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y[i], result[i], tolerance);
+ }
+
+ result = transformer.inversetransform(y);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x[i], result[i], tolerance);
+ }
+
+ FastFourierTransformer.scaleArray(x, FastMath.sqrt(0.5 * (x.length-1)));
+
+ result = transformer.transform2(y);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x[i], result[i], tolerance);
+ }
+
+ result = transformer.inversetransform2(x);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y[i], result[i], tolerance);
+ }
+ }
+
+ /**
+ * Test of transformer for the sine function.
+ */
+ public void testSinFunction() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastCosineTransformer transformer = new FastCosineTransformer();
+ double min, max, result[], tolerance = 1E-12; int N = 9;
+
+ double expected[] = { 0.0, 3.26197262739567, 0.0,
+ -2.17958042710327, 0.0, -0.648846697642915,
+ 0.0, -0.433545502649478, 0.0 };
+ min = 0.0; max = 2.0 * FastMath.PI * N / (N-1);
+ result = transformer.transform(f, min, max, N);
+ for (int i = 0; i < N; i++) {
+ assertEquals(expected[i], result[i], tolerance);
+ }
+
+ min = -FastMath.PI; max = FastMath.PI * (N+1) / (N-1);
+ result = transformer.transform(f, min, max, N);
+ for (int i = 0; i < N; i++) {
+ assertEquals(-expected[i], result[i], tolerance);
+ }
+ }
+
+ /**
+ * Test of parameters for the transformer.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastCosineTransformer transformer = new FastCosineTransformer();
+
+ try {
+ // bad interval
+ transformer.transform(f, 1, -1, 65);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 1);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 64);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/transform/FastFourierTransformerTest.java b/src/test/java/org/apache/commons/math/transform/FastFourierTransformerTest.java
new file mode 100644
index 0000000..4148048
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/transform/FastFourierTransformerTest.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.transform;
+
+import org.apache.commons.math.analysis.*;
+import org.apache.commons.math.complex.*;
+import org.apache.commons.math.util.FastMath;
+import junit.framework.TestCase;
+
+/**
+ * Testcase for fast Fourier transformer.
+ * <p>
+ * FFT algorithm is exact, the small tolerance number is used only
+ * to account for round-off errors.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public final class FastFourierTransformerTest extends TestCase {
+
+ /**
+ * Test of transformer for the ad hoc data taken from Mathematica.
+ */
+ public void testAdHocData() {
+ FastFourierTransformer transformer = new FastFourierTransformer();
+ Complex result[]; double tolerance = 1E-12;
+
+ double x[] = {1.3, 2.4, 1.7, 4.1, 2.9, 1.7, 5.1, 2.7};
+ Complex y[] = {
+ new Complex(21.9, 0.0),
+ new Complex(-2.09497474683058, 1.91507575950825),
+ new Complex(-2.6, 2.7),
+ new Complex(-1.10502525316942, -4.88492424049175),
+ new Complex(0.1, 0.0),
+ new Complex(-1.10502525316942, 4.88492424049175),
+ new Complex(-2.6, -2.7),
+ new Complex(-2.09497474683058, -1.91507575950825)};
+
+ result = transformer.transform(x);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y[i].getReal(), result[i].getReal(), tolerance);
+ assertEquals(y[i].getImaginary(), result[i].getImaginary(), tolerance);
+ }
+
+ result = transformer.inversetransform(y);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x[i], result[i].getReal(), tolerance);
+ assertEquals(0.0, result[i].getImaginary(), tolerance);
+ }
+
+ double x2[] = {10.4, 21.6, 40.8, 13.6, 23.2, 32.8, 13.6, 19.2};
+ FastFourierTransformer.scaleArray(x2, 1.0 / FastMath.sqrt(x2.length));
+ Complex y2[] = y;
+
+ result = transformer.transform2(y2);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x2[i], result[i].getReal(), tolerance);
+ assertEquals(0.0, result[i].getImaginary(), tolerance);
+ }
+
+ result = transformer.inversetransform2(x2);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y2[i].getReal(), result[i].getReal(), tolerance);
+ assertEquals(y2[i].getImaginary(), result[i].getImaginary(), tolerance);
+ }
+ }
+
+ public void test2DData() {
+ FastFourierTransformer transformer = new FastFourierTransformer();
+ double tolerance = 1E-12;
+ Complex[][] input = new Complex[][] {new Complex[] {new Complex(1, 0),
+ new Complex(2, 0)},
+ new Complex[] {new Complex(3, 1),
+ new Complex(4, 2)}};
+ Complex[][] goodOutput = new Complex[][] {new Complex[] {new Complex(5,
+ 1.5), new Complex(-1, -.5)}, new Complex[] {new Complex(-2,
+ -1.5), new Complex(0, .5)}};
+ Complex[][] output = (Complex[][])transformer.mdfft(input, true);
+ Complex[][] output2 = (Complex[][])transformer.mdfft(output, false);
+
+ assertEquals(input.length, output.length);
+ assertEquals(input.length, output2.length);
+ assertEquals(input[0].length, output[0].length);
+ assertEquals(input[0].length, output2[0].length);
+ assertEquals(input[1].length, output[1].length);
+ assertEquals(input[1].length, output2[1].length);
+
+ for (int i = 0; i < input.length; i++) {
+ for (int j = 0; j < input[0].length; j++) {
+ assertEquals(input[i][j].getImaginary(), output2[i][j].getImaginary(),
+ tolerance);
+ assertEquals(input[i][j].getReal(), output2[i][j].getReal(), tolerance);
+ assertEquals(goodOutput[i][j].getImaginary(), output[i][j].getImaginary(),
+ tolerance);
+ assertEquals(goodOutput[i][j].getReal(), output[i][j].getReal(), tolerance);
+ }
+ }
+ }
+
+ /**
+ * Test of transformer for the sine function.
+ */
+ public void testSinFunction() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastFourierTransformer transformer = new FastFourierTransformer();
+ Complex result[]; int N = 1 << 8;
+ double min, max, tolerance = 1E-12;
+
+ min = 0.0; max = 2.0 * FastMath.PI;
+ result = transformer.transform(f, min, max, N);
+ assertEquals(0.0, result[1].getReal(), tolerance);
+ assertEquals(-(N >> 1), result[1].getImaginary(), tolerance);
+ assertEquals(0.0, result[N-1].getReal(), tolerance);
+ assertEquals(N >> 1, result[N-1].getImaginary(), tolerance);
+ for (int i = 0; i < N-1; i += (i == 0 ? 2 : 1)) {
+ assertEquals(0.0, result[i].getReal(), tolerance);
+ assertEquals(0.0, result[i].getImaginary(), tolerance);
+ }
+
+ min = -FastMath.PI; max = FastMath.PI;
+ result = transformer.inversetransform(f, min, max, N);
+ assertEquals(0.0, result[1].getReal(), tolerance);
+ assertEquals(-0.5, result[1].getImaginary(), tolerance);
+ assertEquals(0.0, result[N-1].getReal(), tolerance);
+ assertEquals(0.5, result[N-1].getImaginary(), tolerance);
+ for (int i = 0; i < N-1; i += (i == 0 ? 2 : 1)) {
+ assertEquals(0.0, result[i].getReal(), tolerance);
+ assertEquals(0.0, result[i].getImaginary(), tolerance);
+ }
+ }
+
+ /**
+ * Test of parameters for the transformer.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastFourierTransformer transformer = new FastFourierTransformer();
+
+ try {
+ // bad interval
+ transformer.transform(f, 1, -1, 64);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 0);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 100);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/transform/FastHadamardTransformerTest.java b/src/test/java/org/apache/commons/math/transform/FastHadamardTransformerTest.java
new file mode 100644
index 0000000..09ebed1
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/transform/FastHadamardTransformerTest.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.transform;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit Test for HadamardTransformerTest
+ * @see org.apache.commons.math.transform.FastHadamardTransformer
+ */
+public final class FastHadamardTransformerTest extends TestCase {
+
+ /**
+ * Test of transformer for the a 8-point FHT (means n=8)
+ */
+ public void test8Points() {
+ checkAllTransforms(new int[] { 1, 4, -2, 3, 0, 1, 4, -1 },
+ new int[] { 10, -4, 2, -4, 2, -12, 6, 8 });
+ }
+
+ /**
+ * Test of transformer for the a 4-points FHT (means n=4)
+ */
+ public void test4Points() {
+ checkAllTransforms(new int[] { 1, 2, 3, 4 },
+ new int[] { 10, -2, -4, 0 });
+ }
+
+ /**
+ * Test the inverse transform of an integer vector is not always an integer vector
+ */
+ public void testNoIntInverse() {
+ FastHadamardTransformer transformer = new FastHadamardTransformer();
+ double[] x = transformer.inversetransform(new double[] { 0, 1, 0, 1});
+ assertEquals( 0.5, x[0], 0);
+ assertEquals(-0.5, x[1], 0);
+ assertEquals( 0.0, x[2], 0);
+ assertEquals( 0.0, x[3], 0);
+ }
+
+ /**
+ * Test of transformer for wrong number of points
+ */
+ public void test3Points() {
+ try {
+ new FastHadamardTransformer().transform(new double[3]);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+
+ private void checkAllTransforms(int[]x, int[] y) {
+ checkDoubleTransform(x, y);
+ checkInverseDoubleTransform(x, y);
+ checkIntTransform(x, y);
+ }
+
+ private void checkDoubleTransform(int[]x, int[] y) {
+ // Initiate the transformer
+ FastHadamardTransformer transformer = new FastHadamardTransformer();
+
+ // check double transform
+ double[] dX = new double[x.length];
+ for (int i = 0; i < dX.length; ++i) {
+ dX[i] = x[i];
+ }
+ double dResult[] = transformer.transform(dX);
+ for (int i = 0; i < dResult.length; i++) {
+ // compare computed results to precomputed results
+ assertEquals((double) y[i], dResult[i]);
+ }
+ }
+
+ private void checkIntTransform(int[]x, int[] y) {
+ // Initiate the transformer
+ FastHadamardTransformer transformer = new FastHadamardTransformer();
+
+ // check integer transform
+ int iResult[] = transformer.transform(x);
+ for (int i = 0; i < iResult.length; i++) {
+ // compare computed results to precomputed results
+ assertEquals(y[i], iResult[i]);
+ }
+
+ }
+
+ private void checkInverseDoubleTransform(int[]x, int[] y) {
+ // Initiate the transformer
+ FastHadamardTransformer transformer = new FastHadamardTransformer();
+
+ // check double transform
+ double[] dY = new double[y.length];
+ for (int i = 0; i < dY.length; ++i) {
+ dY[i] = y[i];
+ }
+ double dResult[] = transformer.inversetransform(dY);
+ for (int i = 0; i < dResult.length; i++) {
+ // compare computed results to precomputed results
+ assertEquals((double) x[i], dResult[i]);
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/transform/FastSineTransformerTest.java b/src/test/java/org/apache/commons/math/transform/FastSineTransformerTest.java
new file mode 100644
index 0000000..02a16f0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/transform/FastSineTransformerTest.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.transform;
+
+import org.apache.commons.math.analysis.*;
+import org.apache.commons.math.util.FastMath;
+import junit.framework.TestCase;
+
+/**
+ * Testcase for fast sine transformer.
+ * <p>
+ * FST algorithm is exact, the small tolerance number is used only
+ * to account for round-off errors.
+ *
+ * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
+ */
+public final class FastSineTransformerTest extends TestCase {
+
+ /**
+ * Test of transformer for the ad hoc data.
+ */
+ public void testAdHocData() {
+ FastSineTransformer transformer = new FastSineTransformer();
+ double result[], tolerance = 1E-12;
+
+ double x[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };
+ double y[] = { 0.0, 20.1093579685034, -9.65685424949238,
+ 5.98642305066196, -4.0, 2.67271455167720,
+ -1.65685424949238, 0.795649469518633 };
+
+ result = transformer.transform(x);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y[i], result[i], tolerance);
+ }
+
+ result = transformer.inversetransform(y);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x[i], result[i], tolerance);
+ }
+
+ FastFourierTransformer.scaleArray(x, FastMath.sqrt(x.length / 2.0));
+
+ result = transformer.transform2(y);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(x[i], result[i], tolerance);
+ }
+
+ result = transformer.inversetransform2(x);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(y[i], result[i], tolerance);
+ }
+ }
+
+ /**
+ * Test of transformer for the sine function.
+ */
+ public void testSinFunction() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastSineTransformer transformer = new FastSineTransformer();
+ double min, max, result[], tolerance = 1E-12; int N = 1 << 8;
+
+ min = 0.0; max = 2.0 * FastMath.PI;
+ result = transformer.transform(f, min, max, N);
+ assertEquals(N >> 1, result[2], tolerance);
+ for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
+ assertEquals(0.0, result[i], tolerance);
+ }
+
+ min = -FastMath.PI; max = FastMath.PI;
+ result = transformer.transform(f, min, max, N);
+ assertEquals(-(N >> 1), result[2], tolerance);
+ for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
+ assertEquals(0.0, result[i], tolerance);
+ }
+ }
+
+ /**
+ * Test of parameters for the transformer.
+ */
+ public void testParameters() throws Exception {
+ UnivariateRealFunction f = new SinFunction();
+ FastSineTransformer transformer = new FastSineTransformer();
+
+ try {
+ // bad interval
+ transformer.transform(f, 1, -1, 64);
+ fail("Expecting IllegalArgumentException - bad interval");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 0);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ try {
+ // bad samples number
+ transformer.transform(f, -1, 1, 100);
+ fail("Expecting IllegalArgumentException - bad samples number");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/BigRealFieldTest.java b/src/test/java/org/apache/commons/math/util/BigRealFieldTest.java
new file mode 100644
index 0000000..541a005
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/BigRealFieldTest.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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.math.TestUtils;
+import org.junit.Test;
+
+public class BigRealFieldTest {
+
+ @Test
+ public void testZero() {
+ assertEquals(BigReal.ZERO, BigRealField.getInstance().getZero());
+ }
+
+ @Test
+ public void testOne() {
+ assertEquals(BigReal.ONE, BigRealField.getInstance().getOne());
+ }
+
+ @Test
+ public void testSerial() {
+ // deserializing the singleton should give the singleton itself back
+ BigRealField field = BigRealField.getInstance();
+ assertTrue(field == TestUtils.serializeAndRecover(field));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/BigRealTest.java b/src/test/java/org/apache/commons/math/util/BigRealTest.java
new file mode 100644
index 0000000..d8060a0
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/BigRealTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+
+import org.apache.commons.math.TestUtils;
+import org.junit.Test;
+
+public class BigRealTest {
+
+ @Test
+ public void testConstructor() {
+ assertEquals(1.625, new BigReal(new BigDecimal("1.625")).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(new BigInteger("-5")).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(new BigInteger("-5"), MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(0.125, new BigReal(new BigInteger("125"), 3).doubleValue(), 1.0e-15);
+ assertEquals(0.125, new BigReal(new BigInteger("125"), 3, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(new char[] { '1', '.', '6', '2', '5' }).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(new char[] { 'A', 'A', '1', '.', '6', '2', '5', '9' }, 2, 5).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(new char[] { 'A', 'A', '1', '.', '6', '2', '5', '9' }, 2, 5, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(new char[] { '1', '.', '6', '2', '5' }, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(1.625).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal(1.625, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(-5).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(-5, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(-5l).doubleValue(), 1.0e-15);
+ assertEquals(-5.0, new BigReal(-5l, MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal("1.625").doubleValue(), 1.0e-15);
+ assertEquals(1.625, new BigReal("1.625", MathContext.DECIMAL64).doubleValue(), 1.0e-15);
+ }
+
+ @Test
+ public void testCompareTo() {
+ BigReal first = new BigReal(1.0 / 2.0);
+ BigReal second = new BigReal(1.0 / 3.0);
+ BigReal third = new BigReal(1.0 / 2.0);
+
+ assertEquals(0, first.compareTo(first));
+ assertEquals(0, first.compareTo(third));
+ assertEquals(1, first.compareTo(second));
+ assertEquals(-1, second.compareTo(first));
+
+ }
+
+ public void testAdd() {
+ BigReal a = new BigReal("1.2345678");
+ BigReal b = new BigReal("8.7654321");
+ assertEquals(9.9999999, a.add(b).doubleValue(), 1.0e-15);
+ }
+
+ public void testSubtract() {
+ BigReal a = new BigReal("1.2345678");
+ BigReal b = new BigReal("8.7654321");
+ assertEquals( -7.5308643, a.subtract(b).doubleValue(), 1.0e-15);
+ }
+
+ public void testDivide() {
+ BigReal a = new BigReal("1.0000000000");
+ BigReal b = new BigReal("0.0009765625");
+ assertEquals(1024.0, a.divide(b).doubleValue(), 1.0e-15);
+ }
+
+ public void testMultiply() {
+ BigReal a = new BigReal("1024.0");
+ BigReal b = new BigReal("0.0009765625");
+ assertEquals(1.0, a.multiply(b).doubleValue(), 1.0e-15);
+ }
+
+ @Test
+ public void testDoubleValue() {
+ assertEquals(0.5, new BigReal(0.5).doubleValue(), 1.0e-15);
+ }
+
+ @Test
+ public void testBigDecimalValue() {
+ BigDecimal pi = new BigDecimal("3.1415926535897932384626433832795028841971693993751");
+ assertEquals(pi, new BigReal(pi).bigDecimalValue());
+ assertEquals(new BigDecimal(0.5), new BigReal(1.0 / 2.0).bigDecimalValue());
+ }
+
+ @Test
+ public void testEqualsAndHashCode() {
+ BigReal zero = new BigReal(0.0);
+ BigReal nullReal = null;
+ assertTrue(zero.equals(zero));
+ assertFalse(zero.equals(nullReal));
+ assertFalse(zero.equals(Double.valueOf(0)));
+ BigReal zero2 = new BigReal(0.0);
+ assertTrue(zero.equals(zero2));
+ assertEquals(zero.hashCode(), zero2.hashCode());
+ BigReal one = new BigReal(1.0);
+ assertFalse((one.equals(zero) || zero.equals(one)));
+ assertTrue(one.equals(BigReal.ONE));
+ }
+
+ public void testSerial() {
+ BigReal[] Reals = {
+ new BigReal(3.0), BigReal.ONE, BigReal.ZERO,
+ new BigReal(17), new BigReal(FastMath.PI),
+ new BigReal(-2.5)
+ };
+ for (BigReal Real : Reals) {
+ assertEquals(Real, TestUtils.serializeAndRecover(Real));
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/ContinuedFractionTest.java b/src/test/java/org/apache/commons/math/util/ContinuedFractionTest.java
new file mode 100644
index 0000000..4bf30b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/ContinuedFractionTest.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.util;
+
+import org.apache.commons.math.MathException;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class ContinuedFractionTest extends TestCase {
+ /**
+ * Constructor for ContinuedFractionTest.
+ * @param name
+ */
+ public ContinuedFractionTest(String name) {
+ super(name);
+ }
+
+ public void testGoldenRatio(){
+ ContinuedFraction cf = new ContinuedFraction() {
+
+ @Override
+ public double getA(int n, double x) {
+ return 1.0;
+ }
+
+ @Override
+ public double getB(int n, double x) {
+ return 1.0;
+ }
+ };
+
+ try {
+ double gr = cf.evaluate(0.0, 10e-9);
+ assertEquals(1.61803399, gr, 10e-9);
+ } catch (MathException e) {
+ fail(e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/DefaultTransformerTest.java b/src/test/java/org/apache/commons/math/util/DefaultTransformerTest.java
new file mode 100644
index 0000000..7c02fd5
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/DefaultTransformerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.util;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.TestUtils;
+
+/**
+ * @version $Revision: 1073658 $ $Date: 2011-02-23 10:45:42 +0100 (mer. 23 févr. 2011) $
+ */
+public class DefaultTransformerTest extends TestCase {
+ /**
+ *
+ */
+ public void testTransformDouble() throws Exception {
+ double expected = 1.0;
+ Double input = Double.valueOf(expected);
+ DefaultTransformer t = new DefaultTransformer();
+ assertEquals(expected, t.transform(input), 1.0e-4);
+ }
+
+ /**
+ *
+ */
+ public void testTransformNull() throws Exception {
+ DefaultTransformer t = new DefaultTransformer();
+ try {
+ t.transform(null);
+ fail("Expecting MathException");
+ } catch (MathException e) {
+ // expected
+ }
+ }
+
+ /**
+ *
+ */
+ public void testTransformInteger() throws Exception {
+ double expected = 1.0;
+ Integer input = Integer.valueOf(1);
+ DefaultTransformer t = new DefaultTransformer();
+ assertEquals(expected, t.transform(input), 1.0e-4);
+ }
+
+ /**
+ *
+ */
+ public void testTransformBigDecimal() throws Exception {
+ double expected = 1.0;
+ BigDecimal input = new BigDecimal("1.0");
+ DefaultTransformer t = new DefaultTransformer();
+ assertEquals(expected, t.transform(input), 1.0e-4);
+ }
+
+ /**
+ *
+ */
+ public void testTransformString() throws Exception {
+ double expected = 1.0;
+ String input = "1.0";
+ DefaultTransformer t = new DefaultTransformer();
+ assertEquals(expected, t.transform(input), 1.0e-4);
+ }
+
+ /**
+ *
+ */
+ public void testTransformObject(){
+ Boolean input = Boolean.TRUE;
+ DefaultTransformer t = new DefaultTransformer();
+ try {
+ t.transform(input);
+ fail("Expecting MathException");
+ } catch (MathException e) {
+ // expected
+ }
+ }
+
+ public void testSerial() {
+ assertEquals(new DefaultTransformer(), TestUtils.serializeAndRecover(new DefaultTransformer()));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/DoubleArrayAbstractTest.java b/src/test/java/org/apache/commons/math/util/DoubleArrayAbstractTest.java
new file mode 100644
index 0000000..23c5cca
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/DoubleArrayAbstractTest.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.util;
+
+import org.apache.commons.math.stat.StatUtils;
+
+import junit.framework.TestCase;
+
+/**
+ * This class contains test cases for the ExpandableDoubleArray.
+ *
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public abstract class DoubleArrayAbstractTest extends TestCase {
+
+ protected DoubleArray da = null;
+
+ // Array used to test rolling
+ protected DoubleArray ra = null;
+
+ public DoubleArrayAbstractTest(String name) {
+ super(name);
+ }
+
+ public void testAdd1000() {
+
+ for (int i = 0; i < 1000; i++) {
+ da.addElement(i);
+ }
+
+ assertEquals(
+ "Number of elements should be equal to 1000 after adding 1000 values",
+ 1000,
+ da.getNumElements());
+
+ assertEquals(
+ "The element at the 56th index should be 56",
+ 56.0,
+ da.getElement(56),
+ Double.MIN_VALUE);
+
+ }
+
+ public void testGetValues() {
+ double[] controlArray = { 2.0, 4.0, 6.0 };
+
+ da.addElement(2.0);
+ da.addElement(4.0);
+ da.addElement(6.0);
+ double[] testArray = da.getElements();
+
+ for (int i = 0; i < da.getNumElements(); i++) {
+ assertEquals(
+ "The testArray values should equal the controlArray values, index i: "
+ + i
+ + " does not match",
+ testArray[i],
+ controlArray[i],
+ Double.MIN_VALUE);
+ }
+
+ }
+
+ public void testAddElementRolling() {
+ ra.addElement(0.5);
+ ra.addElement(1.0);
+ ra.addElement(1.0);
+ ra.addElement(1.0);
+ ra.addElement(1.0);
+ ra.addElement(1.0);
+ ra.addElementRolling(2.0);
+
+ assertEquals(
+ "There should be 6 elements in the eda",
+ 6,
+ ra.getNumElements());
+ assertEquals(
+ "The max element should be 2.0",
+ 2.0,
+ StatUtils.max(ra.getElements()),
+ Double.MIN_VALUE);
+ assertEquals(
+ "The min element should be 1.0",
+ 1.0,
+ StatUtils.min(ra.getElements()),
+ Double.MIN_VALUE);
+
+ for (int i = 0; i < 1024; i++) {
+ ra.addElementRolling(i);
+ }
+
+ assertEquals(
+ "We just inserted 1024 rolling elements, num elements should still be 6",
+ 6,
+ ra.getNumElements());
+ }
+
+ public void testMinMax() {
+ da.addElement(2.0);
+ da.addElement(22.0);
+ da.addElement(-2.0);
+ da.addElement(21.0);
+ da.addElement(22.0);
+ da.addElement(42.0);
+ da.addElement(62.0);
+ da.addElement(22.0);
+ da.addElement(122.0);
+ da.addElement(1212.0);
+
+ assertEquals("Min should be -2.0", -2.0, StatUtils.min(da.getElements()), Double.MIN_VALUE);
+ assertEquals(
+ "Max should be 1212.0",
+ 1212.0,
+ StatUtils.max(da.getElements()),
+ Double.MIN_VALUE);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/FastMathStrictComparisonTest.java b/src/test/java/org/apache/commons/math/util/FastMathStrictComparisonTest.java
new file mode 100644
index 0000000..59ad385
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/FastMathStrictComparisonTest.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test to compare FastMath results against StrictMath results for boundary values.
+ * <p>
+ * Running all tests independently: <br/>
+ * {@code mvn test -Dtest=FastMathStrictComparisonTest}<br/>
+ * or just run tests against a single method (e.g. scalb):<br/>
+ * {@code mvn test -Dtest=FastMathStrictComparisonTest -DargLine="-DtestMethod=scalb"}
+ */
+ at RunWith(Parameterized.class)
+public class FastMathStrictComparisonTest {
+
+ // Values which often need special handling
+ private static final Double[] DOUBLE_SPECIAL_VALUES = {
+ -0.0, +0.0, // 1,2
+ Double.NaN, // 3
+ Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, // 4,5
+ -Double.MAX_VALUE, Double.MAX_VALUE, // 6,7
+ // decreasing order of absolute value to help catch first failure
+ -MathUtils.EPSILON, MathUtils.EPSILON, // 8,9
+ -MathUtils.SAFE_MIN, MathUtils.SAFE_MIN, // 10,11
+ -Double.MIN_VALUE, Double.MIN_VALUE, // 12,13
+ };
+
+ private static final Float [] FLOAT_SPECIAL_VALUES = {
+ -0.0f, +0.0f, // 1,2
+ Float.NaN, // 3
+ Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, // 4,5
+ Float.MIN_VALUE, Float.MAX_VALUE, // 6,7
+ -Float.MIN_VALUE, -Float.MAX_VALUE, // 8,9
+ };
+
+ private static final Object [] LONG_SPECIAL_VALUES = {
+ -1,0,1, // 1,2,3
+ Long.MIN_VALUE, Long.MAX_VALUE, // 4,5
+ };
+
+ private static final Object[] INT_SPECIAL_VALUES = {
+ -1,0,1, // 1,2,3
+ Integer.MIN_VALUE, Integer.MAX_VALUE, // 4,5
+ };
+
+ private final Method mathMethod;
+ private final Method fastMethod;
+ private final Type[] types;
+ private final Object[][] valueArrays;
+
+ public FastMathStrictComparisonTest(Method m, Method f, Type[] types, Object[][] data) throws Exception{
+ this.mathMethod=m;
+ this.fastMethod=f;
+ this.types=types;
+ this.valueArrays=data;
+ }
+
+ @Test
+ public void test1() throws Exception{
+ setupMethodCall(mathMethod, fastMethod, types, valueArrays);
+ }
+ private static boolean isNumber(Double d) {
+ return !(d.isInfinite() || d.isNaN());
+ }
+
+ private static boolean isNumber(Float f) {
+ return !(f.isInfinite() || f.isNaN());
+ }
+
+ private static void reportFailedResults(Method mathMethod, Object[] params, Object expected, Object actual, int[] entries){
+ final String methodName = mathMethod.getName();
+ String format = null;
+ long actL=0;
+ long expL=0;
+ if (expected instanceof Double) {
+ Double exp = (Double) expected;
+ Double act = (Double) actual;
+ if (isNumber(exp) && isNumber(act) && exp != 0) { // show difference as hex
+ actL = Double.doubleToLongBits(act);
+ expL = Double.doubleToLongBits(exp);
+ if (Math.abs(actL-expL)==1) {
+ // Not 100% sure off-by-one errors are allowed everywhere, so only allow for these methods
+ if (methodName.equals("toRadians") || methodName.equals("atan2")) {
+ return;
+ }
+ }
+ format = "%016x";
+ }
+ } else if (expected instanceof Float ){
+ Float exp = (Float) expected;
+ Float act = (Float) actual;
+ if (isNumber(exp) && isNumber(act) && exp != 0) { // show difference as hex
+ actL = Float.floatToIntBits(act);
+ expL = Float.floatToIntBits(exp);
+ format = "%08x";
+ }
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(mathMethod.getReturnType().getSimpleName());
+ sb.append(" ");
+ sb.append(methodName);
+ sb.append("(");
+ String sep = "";
+ for(Object o : params){
+ sb.append(sep);
+ sb.append(o);
+ sep=", ";
+ }
+ sb.append(") expected ");
+ if (format != null){
+ sb.append(String.format(format, expL));
+ } else {
+ sb.append(expected);
+ }
+ sb.append(" actual ");
+ if (format != null){
+ sb.append(String.format(format, actL));
+ } else {
+ sb.append(actual);
+ }
+ sb.append(" entries ");
+ sb.append(Arrays.toString(entries));
+ String message = sb.toString();
+ final boolean fatal = true;
+ if (fatal) {
+ Assert.fail(message);
+ } else {
+ System.out.println(message);
+ }
+ }
+
+ private static void callMethods(Method mathMethod, Method fastMethod,
+ Object[] params, int[] entries) throws IllegalAccessException,
+ InvocationTargetException {
+ try {
+ Object expected = mathMethod.invoke(mathMethod, params);
+ Object actual = fastMethod.invoke(mathMethod, params);
+ if (!expected.equals(actual)) {
+ reportFailedResults(mathMethod, params, expected, actual, entries);
+ }
+ } catch (IllegalArgumentException e) {
+ Assert.fail(mathMethod+" "+e);
+ }
+ }
+
+ private static void setupMethodCall(Method mathMethod, Method fastMethod,
+ Type[] types, Object[][] valueArrays) throws Exception {
+ Object[] params = new Object[types.length];
+ int entry1 = 0;
+ int[] entries = new int[types.length];
+ for(Object d : valueArrays[0]) {
+ entry1++;
+ params[0] = d;
+ entries[0] = entry1;
+ if (params.length > 1){
+ int entry2 = 0;
+ for(Object d1 : valueArrays[1]) {
+ entry2++;
+ params[1] = d1;
+ entries[1] = entry2;
+ callMethods(mathMethod, fastMethod, params, entries);
+ }
+ } else {
+ callMethods(mathMethod, fastMethod, params, entries);
+ }
+ }
+ }
+
+ @Parameters
+ public static List<Object[]> data() throws Exception {
+ String singleMethod = System.getProperty("testMethod");
+ List<Object[]> list = new ArrayList<Object[]>();
+ for(Method mathMethod : StrictMath.class.getDeclaredMethods()) {
+ method:
+ if (Modifier.isPublic(mathMethod.getModifiers())){// Only test public methods
+ Type []types = mathMethod.getGenericParameterTypes();
+ if (types.length >=1) { // Only check methods with at least one parameter
+ try {
+ // Get the corresponding FastMath method
+ Method fastMethod = FastMath.class.getDeclaredMethod(mathMethod.getName(), (Class[]) types);
+ if (Modifier.isPublic(fastMethod.getModifiers())) { // It must be public too
+ if (singleMethod != null && !fastMethod.getName().equals(singleMethod)) {
+ break method;
+ }
+ Object [][] values = new Object[types.length][];
+ int index = 0;
+ for(Type t : types) {
+ if (t.equals(double.class)){
+ values[index]=DOUBLE_SPECIAL_VALUES;
+ } else if (t.equals(float.class)) {
+ values[index]=FLOAT_SPECIAL_VALUES;
+ } else if (t.equals(long.class)) {
+ values[index]=LONG_SPECIAL_VALUES;
+ } else if (t.equals(int.class)) {
+ values[index]=INT_SPECIAL_VALUES;
+ } else {
+ System.out.println("Cannot handle class "+t+" for "+mathMethod);
+ break method;
+ }
+ index++;
+ }
+// System.out.println(fastMethod);
+ /*
+ * The current implementation runs each method as a separate test.
+ * Could be amended to run each value as a separate test
+ */
+ list.add(new Object[]{mathMethod, fastMethod, types, values});
+// setupMethodCall(mathMethod, fastMethod, params, data);
+ } else {
+ System.out.println("Cannot find public FastMath method corresponding to: "+mathMethod);
+ }
+ } catch (NoSuchMethodException e) {
+ System.out.println("Cannot find FastMath method corresponding to: "+mathMethod);
+ }
+ }
+ }
+ }
+ return list;
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/FastMathTest.java b/src/test/java/org/apache/commons/math/util/FastMathTest.java
new file mode 100644
index 0000000..7c708e2
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/FastMathTest.java
@@ -0,0 +1,1118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+
+import org.apache.commons.math.dfp.Dfp;
+import org.apache.commons.math.dfp.DfpField;
+import org.apache.commons.math.dfp.DfpMath;
+import org.apache.commons.math.random.MersenneTwister;
+import org.apache.commons.math.random.RandomGenerator;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class FastMathTest {
+
+ private static final double MAX_ERROR_ULP = 0.51;
+ private static final int NUMBER_OF_TRIALS = 1000;
+
+
+ private DfpField field;
+ private RandomGenerator generator;
+
+ @Before
+ public void setUp() {
+ field = new DfpField(40);
+ generator = new MersenneTwister(6176597458463500194l);
+ }
+
+ @Test
+ public void testMinMaxDouble() {
+ double[][] pairs = {
+ { -50.0, 50.0 },
+ { Double.POSITIVE_INFINITY, 1.0 },
+ { Double.NEGATIVE_INFINITY, 1.0 },
+ { Double.NaN, 1.0 },
+ { Double.POSITIVE_INFINITY, 0.0 },
+ { Double.NEGATIVE_INFINITY, 0.0 },
+ { Double.NaN, 0.0 },
+ { Double.NaN, Double.NEGATIVE_INFINITY },
+ { Double.NaN, Double.POSITIVE_INFINITY },
+ { MathUtils.SAFE_MIN, MathUtils.EPSILON }
+ };
+ for (double[] pair : pairs) {
+ Assert.assertEquals("min(" + pair[0] + ", " + pair[1] + ")",
+ Math.min(pair[0], pair[1]),
+ FastMath.min(pair[0], pair[1]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("min(" + pair[1] + ", " + pair[0] + ")",
+ Math.min(pair[1], pair[0]),
+ FastMath.min(pair[1], pair[0]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("max(" + pair[0] + ", " + pair[1] + ")",
+ Math.max(pair[0], pair[1]),
+ FastMath.max(pair[0], pair[1]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("max(" + pair[1] + ", " + pair[0] + ")",
+ Math.max(pair[1], pair[0]),
+ FastMath.max(pair[1], pair[0]),
+ MathUtils.EPSILON);
+ }
+ }
+
+ @Test
+ public void testMinMaxFloat() {
+ float[][] pairs = {
+ { -50.0f, 50.0f },
+ { Float.POSITIVE_INFINITY, 1.0f },
+ { Float.NEGATIVE_INFINITY, 1.0f },
+ { Float.NaN, 1.0f },
+ { Float.POSITIVE_INFINITY, 0.0f },
+ { Float.NEGATIVE_INFINITY, 0.0f },
+ { Float.NaN, 0.0f },
+ { Float.NaN, Float.NEGATIVE_INFINITY },
+ { Float.NaN, Float.POSITIVE_INFINITY }
+ };
+ for (float[] pair : pairs) {
+ Assert.assertEquals("min(" + pair[0] + ", " + pair[1] + ")",
+ Math.min(pair[0], pair[1]),
+ FastMath.min(pair[0], pair[1]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("min(" + pair[1] + ", " + pair[0] + ")",
+ Math.min(pair[1], pair[0]),
+ FastMath.min(pair[1], pair[0]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("max(" + pair[0] + ", " + pair[1] + ")",
+ Math.max(pair[0], pair[1]),
+ FastMath.max(pair[0], pair[1]),
+ MathUtils.EPSILON);
+ Assert.assertEquals("max(" + pair[1] + ", " + pair[0] + ")",
+ Math.max(pair[1], pair[0]),
+ FastMath.max(pair[1], pair[0]),
+ MathUtils.EPSILON);
+ }
+ }
+
+ @Test
+ public void testConstants() {
+ Assert.assertEquals(Math.PI, FastMath.PI, 1.0e-20);
+ Assert.assertEquals(Math.E, FastMath.E, 1.0e-20);
+ }
+
+ @Test
+ public void testAtan2() {
+ double y1 = 1.2713504628280707e10;
+ double x1 = -5.674940885228782e-10;
+ Assert.assertEquals(Math.atan2(y1, x1), FastMath.atan2(y1, x1), 2 * MathUtils.EPSILON);
+ double y2 = 0.0;
+ double x2 = Double.POSITIVE_INFINITY;
+ Assert.assertEquals(Math.atan2(y2, x2), FastMath.atan2(y2, x2), MathUtils.SAFE_MIN);
+ }
+
+ @Test
+ public void testHyperbolic() {
+ double maxErr = 0;
+ for (double x = -30; x < 30; x += 0.001) {
+ double tst = FastMath.sinh(x);
+ double ref = Math.sinh(x);
+ maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
+ }
+ Assert.assertEquals(0, maxErr, 2);
+
+ maxErr = 0;
+ for (double x = -30; x < 30; x += 0.001) {
+ double tst = FastMath.cosh(x);
+ double ref = Math.cosh(x);
+ maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
+ }
+ Assert.assertEquals(0, maxErr, 2);
+
+ maxErr = 0;
+ for (double x = -0.5; x < 0.5; x += 0.001) {
+ double tst = FastMath.tanh(x);
+ double ref = Math.tanh(x);
+ maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
+ }
+ Assert.assertEquals(0, maxErr, 4);
+
+ }
+
+ @Test
+ public void testHyperbolicInverses() {
+ double maxErr = 0;
+ for (double x = -30; x < 30; x += 0.01) {
+ maxErr = FastMath.max(maxErr, FastMath.abs(x - FastMath.sinh(FastMath.asinh(x))) / (2 * FastMath.ulp(x)));
+ }
+ Assert.assertEquals(0, maxErr, 3);
+
+ maxErr = 0;
+ for (double x = 1; x < 30; x += 0.01) {
+ maxErr = FastMath.max(maxErr, FastMath.abs(x - FastMath.cosh(FastMath.acosh(x))) / (2 * FastMath.ulp(x)));
+ }
+ Assert.assertEquals(0, maxErr, 2);
+
+ maxErr = 0;
+ for (double x = -1 + MathUtils.EPSILON; x < 1 - MathUtils.EPSILON; x += 0.0001) {
+ maxErr = FastMath.max(maxErr, FastMath.abs(x - FastMath.tanh(FastMath.atanh(x))) / (2 * FastMath.ulp(x)));
+ }
+ Assert.assertEquals(0, maxErr, 2);
+
+ }
+
+ @Test
+ public void testLogAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = Math.exp(generator.nextDouble() * 1416.0 - 708.0) * generator.nextDouble();
+ // double x = generator.nextDouble()*2.0;
+ double tst = FastMath.log(x);
+ double ref = DfpMath.log(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0.0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double
+ .doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("log() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testLog10Accuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = Math.exp(generator.nextDouble() * 1416.0 - 708.0) * generator.nextDouble();
+ // double x = generator.nextDouble()*2.0;
+ double tst = FastMath.log10(x);
+ double ref = DfpMath.log(field.newDfp(x)).divide(DfpMath.log(field.newDfp("10"))).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0.0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x)).divide(DfpMath.log(field.newDfp("10")))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("log10() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testLog1pAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = Math.exp(generator.nextDouble() * 10.0 - 5.0) * generator.nextDouble();
+ // double x = generator.nextDouble()*2.0;
+ double tst = FastMath.log1p(x);
+ double ref = DfpMath.log(field.newDfp(x).add(field.getOne())).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0.0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.log(field.newDfp(x).add(field.getOne()))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("log1p() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testLogSpecialCases() {
+ double x;
+
+ x = FastMath.log(0.0);
+ if (x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("Log of zero should be -Inf");
+
+ x = FastMath.log(-0.0);
+ if (x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("Log of zero should be -Inf");
+
+ x = FastMath.log(Double.NaN);
+ if (x == x)
+ throw new RuntimeException("Log of NaN should be NaN");
+
+ x = FastMath.log(-1.0);
+ if (x == x)
+ throw new RuntimeException("Log of negative number should be NaN");
+
+ x = FastMath.log(Double.MIN_VALUE);
+ if (x != -744.4400719213812)
+ throw new RuntimeException(
+ "Log of Double.MIN_VALUE should be -744.4400719213812");
+
+ x = FastMath.log(-1.0);
+ if (x == x)
+ throw new RuntimeException("Log of negative number should be NaN");
+
+ x = FastMath.log(Double.POSITIVE_INFINITY);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("Log of infinity should be infinity");
+ }
+
+ @Test
+ public void testExpSpecialCases() {
+ double x;
+
+ /* Smallest value that will round up to Double.MIN_VALUE */
+ x = FastMath.exp(-745.1332191019411);
+ if (x != Double.MIN_VALUE)
+ throw new RuntimeException(
+ "exp(-745.1332191019411) should be Double.MIN_VALUE");
+
+ x = FastMath.exp(-745.1332191019412);
+ if (x != 0.0)
+ throw new RuntimeException("exp(-745.1332191019412) should be 0.0");
+
+ x = FastMath.exp(Double.NaN);
+ if (x == x)
+ throw new RuntimeException("exp of NaN should be NaN");
+
+ x = FastMath.exp(Double.POSITIVE_INFINITY);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("exp of infinity should be infinity");
+
+ x = FastMath.exp(Double.NEGATIVE_INFINITY);
+ if (x != 0.0)
+ throw new RuntimeException("exp of -infinity should be 0.0");
+
+ x = FastMath.exp(1.0);
+ if (x != Math.E)
+ throw new RuntimeException("exp(1) should be Math.E");
+ }
+
+ @Test
+ public void testPowSpecialCases() {
+ double x;
+
+ x = FastMath.pow(-1.0, 0.0);
+ if (x != 1.0)
+ throw new RuntimeException("pow(x, 0) should be 1.0");
+
+ x = FastMath.pow(-1.0, -0.0);
+ if (x != 1.0)
+ throw new RuntimeException("pow(x, -0) should be 1.0");
+
+ x = FastMath.pow(Math.PI, 1.0);
+ if (x != Math.PI)
+ throw new RuntimeException("pow(PI, 1.0) should be PI");
+
+ x = FastMath.pow(-Math.PI, 1.0);
+ if (x != -Math.PI)
+ throw new RuntimeException("pow(-PI, 1.0) should be PI");
+
+ x = FastMath.pow(Math.PI, Double.NaN);
+ if (x == x)
+ throw new RuntimeException("pow(PI, NaN) should be NaN");
+
+ x = FastMath.pow(Double.NaN, Math.PI);
+ if (x == x)
+ throw new RuntimeException("pow(NaN, PI) should be NaN");
+
+ x = FastMath.pow(2.0, Double.POSITIVE_INFINITY);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(2.0, Infinity) should be Infinity");
+
+ x = FastMath.pow(0.5, Double.NEGATIVE_INFINITY);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(0.5, -Infinity) should be Infinity");
+
+ x = FastMath.pow(0.5, Double.POSITIVE_INFINITY);
+ if (x != 0.0)
+ throw new RuntimeException("pow(0.5, Infinity) should be 0.0");
+
+ x = FastMath.pow(2.0, Double.NEGATIVE_INFINITY);
+ if (x != 0.0)
+ throw new RuntimeException("pow(2.0, -Infinity) should be 0.0");
+
+ x = FastMath.pow(0.0, 0.5);
+ if (x != 0.0)
+ throw new RuntimeException("pow(0.0, 0.5) should be 0.0");
+
+ x = FastMath.pow(Double.POSITIVE_INFINITY, -0.5);
+ if (x != 0.0)
+ throw new RuntimeException("pow(Inf, -0.5) should be 0.0");
+
+ x = FastMath.pow(0.0, -0.5);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(0.0, -0.5) should be Inf");
+
+ x = FastMath.pow(Double.POSITIVE_INFINITY, 0.5);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(Inf, 0.5) should be Inf");
+
+ x = FastMath.pow(-0.0, -3.0);
+ if (x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("pow(-0.0, -3.0) should be -Inf");
+
+ x = FastMath.pow(Double.NEGATIVE_INFINITY, 3.0);
+ if (x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("pow(-Inf, -3.0) should be -Inf");
+
+ x = FastMath.pow(-0.0, -3.5);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(-0.0, -3.5) should be Inf");
+
+ x = FastMath.pow(Double.POSITIVE_INFINITY, 3.5);
+ if (x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("pow(Inf, 3.5) should be Inf");
+
+ x = FastMath.pow(-2.0, 3.0);
+ if (x != -8.0)
+ throw new RuntimeException("pow(-2.0, 3.0) should be -8.0");
+
+ x = FastMath.pow(-2.0, 3.5);
+ if (x == x)
+ throw new RuntimeException("pow(-2.0, 3.5) should be NaN");
+ }
+
+ @Test
+ public void testAtan2SpecialCases() {
+ double x;
+
+ x = FastMath.atan2(Double.NaN, 0.0);
+ if (x == x)
+ throw new RuntimeException("atan2(NaN, 0.0) should be NaN");
+
+ x = FastMath.atan2(0.0, Double.NaN);
+ if (x == x)
+ throw new RuntimeException("atan2(0.0, NaN) should be NaN");
+
+ x = FastMath.atan2(0.0, 0.0);
+ if (x != 0.0 || 1 / x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("atan2(0.0, 0.0) should be 0.0");
+
+ x = FastMath.atan2(0.0, 0.001);
+ if (x != 0.0 || 1 / x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("atan2(0.0, 0.001) should be 0.0");
+
+ x = FastMath.atan2(0.1, Double.POSITIVE_INFINITY);
+ if (x != 0.0 || 1 / x != Double.POSITIVE_INFINITY)
+ throw new RuntimeException("atan2(0.1, +Inf) should be 0.0");
+
+ x = FastMath.atan2(-0.0, 0.0);
+ if (x != 0.0 || 1 / x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("atan2(-0.0, 0.0) should be -0.0");
+
+ x = FastMath.atan2(-0.0, 0.001);
+ if (x != 0.0 || 1 / x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("atan2(-0.0, 0.001) should be -0.0");
+
+ x = FastMath.atan2(-0.1, Double.POSITIVE_INFINITY);
+ if (x != 0.0 || 1 / x != Double.NEGATIVE_INFINITY)
+ throw new RuntimeException("atan2(-0.0, +Inf) should be -0.0");
+
+ x = FastMath.atan2(0.0, -0.0);
+ if (x != Math.PI)
+ throw new RuntimeException("atan2(0.0, -0.0) should be PI");
+
+ x = FastMath.atan2(0.1, Double.NEGATIVE_INFINITY);
+ if (x != Math.PI)
+ throw new RuntimeException("atan2(0.1, -Inf) should be PI");
+
+ x = FastMath.atan2(-0.0, -0.0);
+ if (x != -Math.PI)
+ throw new RuntimeException("atan2(-0.0, -0.0) should be -PI");
+
+ x = FastMath.atan2(-0.1, Double.NEGATIVE_INFINITY);
+ if (x != -Math.PI)
+ throw new RuntimeException("atan2(0.1, -Inf) should be -PI");
+
+ x = FastMath.atan2(0.1, 0.0);
+ if (x != Math.PI / 2)
+ throw new RuntimeException("atan2(0.1, 0.0) should be PI/2");
+
+ x = FastMath.atan2(0.1, -0.0);
+ if (x != Math.PI / 2)
+ throw new RuntimeException("atan2(0.1, -0.0) should be PI/2");
+
+ x = FastMath.atan2(Double.POSITIVE_INFINITY, 0.1);
+ if (x != Math.PI / 2)
+ throw new RuntimeException("atan2(Inf, 0.1) should be PI/2");
+
+ x = FastMath.atan2(Double.POSITIVE_INFINITY, -0.1);
+ if (x != Math.PI / 2)
+ throw new RuntimeException("atan2(Inf, -0.1) should be PI/2");
+
+ x = FastMath.atan2(-0.1, 0.0);
+ if (x != -Math.PI / 2)
+ throw new RuntimeException("atan2(-0.1, 0.0) should be -PI/2");
+
+ x = FastMath.atan2(-0.1, -0.0);
+ if (x != -Math.PI / 2)
+ throw new RuntimeException("atan2(-0.1, -0.0) should be -PI/2");
+
+ x = FastMath.atan2(Double.NEGATIVE_INFINITY, 0.1);
+ if (x != -Math.PI / 2)
+ throw new RuntimeException("atan2(-Inf, 0.1) should be -PI/2");
+
+ x = FastMath.atan2(Double.NEGATIVE_INFINITY, -0.1);
+ if (x != -Math.PI / 2)
+ throw new RuntimeException("atan2(-Inf, -0.1) should be -PI/2");
+
+ x = FastMath.atan2(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ if (x != Math.PI / 4)
+ throw new RuntimeException("atan2(Inf, Inf) should be PI/4");
+
+ x = FastMath.atan2(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+ if (x != Math.PI * 3.0 / 4.0)
+ throw new RuntimeException("atan2(Inf, -Inf) should be PI * 3/4");
+
+ x = FastMath.atan2(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+ if (x != -Math.PI / 4)
+ throw new RuntimeException("atan2(-Inf, Inf) should be -PI/4");
+
+ x = FastMath.atan2(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+ if (x != -Math.PI * 3.0 / 4.0)
+ throw new RuntimeException("atan2(-Inf, -Inf) should be -PI * 3/4");
+ }
+
+ @Test
+ public void testPowAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = (generator.nextDouble() * 2.0 + 0.25);
+ double y = (generator.nextDouble() * 1200.0 - 600.0) * generator.nextDouble();
+ /*
+ * double x = FastMath.floor(generator.nextDouble()*1024.0 - 512.0); double
+ * y; if (x != 0) y = FastMath.floor(512.0 / FastMath.abs(x)); else
+ * y = generator.nextDouble()*1200.0; y = y - y/2; x = FastMath.pow(2.0, x) *
+ * generator.nextDouble(); y = y * generator.nextDouble();
+ */
+
+ // double x = generator.nextDouble()*2.0;
+ double tst = FastMath.pow(x, y);
+ double ref = DfpMath.pow(field.newDfp(x), field.newDfp(y)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double
+ .doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.pow(field.newDfp(x), field.newDfp(y))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + y + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("pow() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testExpAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.exp(x);
+ double ref = DfpMath.exp(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("exp() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testSinAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) *
+ Math.pow(2, 21) * generator.nextDouble();
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.sin(x);
+ double ref = DfpMath.sin(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.sin(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("sin() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testCosAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) *
+ Math.pow(2, 21) * generator.nextDouble();
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.cos(x);
+ double ref = DfpMath.cos(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.cos(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("cos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testTanAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ double x = ((generator.nextDouble() * Math.PI) - Math.PI / 2.0) *
+ Math.pow(2, 12) * generator.nextDouble();
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.tan(x);
+ double ref = DfpMath.tan(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.tan(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("tan() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testAtanAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ // double x = ((generator.nextDouble() * Math.PI) - Math.PI/2.0) *
+ // generator.nextDouble();
+ double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble();
+
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.atan(x);
+ double ref = DfpMath.atan(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.atan(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("atan() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testAtan2Accuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = ((generator.nextDouble() * 1416.0) - 708.0) * generator.nextDouble();
+ double x = generator.nextDouble() - 0.5;
+ double y = generator.nextDouble() - 0.5;
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ // double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.atan2(y, x);
+ Dfp refdfp = DfpMath.atan(field.newDfp(y)
+ .divide(field.newDfp(x)));
+ /* Make adjustments for sign */
+ if (x < 0.0) {
+ if (y > 0.0)
+ refdfp = field.getPi().add(refdfp);
+ else
+ refdfp = refdfp.subtract(field.getPi());
+ }
+
+ double ref = refdfp.toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double
+ .doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(refdfp).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + y + "\t" + tst + "\t" + ref + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("atan2() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testExpm1Accuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ /* double x = 1.0 + i/1024.0/2.0; */
+ // double x = (generator.nextDouble() * 20.0) - 10.0;
+ double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble();
+ /* double x = 3.0 / 512.0 * i - 3.0; */
+ double tst = FastMath.expm1(x);
+ double ref = DfpMath.exp(field.newDfp(x)).subtract(field.getOne()).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double
+ .doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("expm1() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testAsinAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+
+ double tst = FastMath.asin(x);
+ double ref = DfpMath.asin(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.asin(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("asin() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testAcosAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 2.0) - 1.0) * generator.nextDouble();
+
+ double tst = FastMath.acos(x);
+ double ref = DfpMath.acos(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.acos(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("acos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ private Dfp cosh(Dfp x) {
+ return DfpMath.exp(x).add(DfpMath.exp(x.negate())).divide(2);
+ }
+
+ private Dfp sinh(Dfp x) {
+ return DfpMath.exp(x).subtract(DfpMath.exp(x.negate())).divide(2);
+ }
+
+ private Dfp tanh(Dfp x) {
+ return sinh(x).divide(cosh(x));
+ }
+
+ @Test
+ public void testSinhAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble();
+
+ double tst = FastMath.sinh(x);
+ double ref = sinh(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(sinh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("sinh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testCoshAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble();
+
+ double tst = FastMath.cosh(x);
+ double ref = cosh(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(cosh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("cosh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testTanhAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 16.0) - 8.0) * generator.nextDouble();
+
+ double tst = FastMath.tanh(x);
+ double ref = tanh(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(tanh(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("tanh() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ @Test
+ public void testCbrtAccuracy() {
+ double maxerrulp = 0.0;
+
+ for (int i=0; i<10000; i++) {
+ double x = ((generator.nextDouble() * 200.0) - 100.0) * generator.nextDouble();
+
+ double tst = FastMath.cbrt(x);
+ double ref = cbrt(field.newDfp(x)).toDouble();
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(cbrt(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
+ //System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+
+ Assert.assertTrue("cbrt() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+ }
+
+ private Dfp cbrt(Dfp x) {
+ boolean negative=false;
+
+ if (x.lessThan(field.getZero())) {
+ negative = true;
+ x = x.negate();
+ }
+
+ Dfp y = DfpMath.pow(x, field.getOne().divide(3));
+
+ if (negative) {
+ y = y.negate();
+ }
+
+ return y;
+ }
+
+ @Test
+ public void testToDegrees() {
+ double maxerrulp = 0.0;
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = generator.nextDouble();
+ double tst = field.newDfp(x).multiply(180).divide(field.getPi()).toDouble();
+ double ref = FastMath.toDegrees(x);
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+ Assert.assertTrue("toDegrees() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+
+ }
+
+ @Test
+ public void testToRadians() {
+ double maxerrulp = 0.0;
+ for (int i = 0; i < NUMBER_OF_TRIALS; i++) {
+ double x = generator.nextDouble();
+ double tst = field.newDfp(x).multiply(field.getPi()).divide(180).toDouble();
+ double ref = FastMath.toRadians(x);
+ double err = (tst - ref) / ref;
+
+ if (err != 0) {
+ double ulp = Math.abs(ref -
+ Double.longBitsToDouble((Double
+ .doubleToLongBits(ref) ^ 1)));
+ double errulp = field.newDfp(tst).subtract(DfpMath.exp(field.newDfp(x)).subtract(field.getOne())).divide(field.newDfp(ulp)).toDouble();
+// System.out.println(x + "\t" + tst + "\t" + ref + "\t" + err + "\t" + errulp);
+
+ maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
+ }
+ }
+ Assert.assertTrue("toRadians() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
+
+ }
+
+ @Test
+ public void testNextAfter() {
+ // 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000
+ Assert.assertEquals(16.0, FastMath.nextAfter(15.999999999999998, 34.27555555555555), 0.0);
+
+ // 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe
+ Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 34.27555555555555), 0.0);
+
+ // 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe
+ Assert.assertEquals(15.999999999999996, FastMath.nextAfter(15.999999999999998, 2.142222222222222), 0.0);
+
+ // 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe
+ Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 2.142222222222222), 0.0);
+
+ // 0x4020000000000000 0x404123456789abcd -> 4020000000000001
+ Assert.assertEquals(8.000000000000002, FastMath.nextAfter(8.0, 34.27555555555555), 0.0);
+
+ // 0xc020000000000000 0x404123456789abcd -> c01fffffffffffff
+ Assert.assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 34.27555555555555), 0.0);
+
+ // 0x4020000000000000 0x400123456789abcd -> 401fffffffffffff
+ Assert.assertEquals(7.999999999999999, FastMath.nextAfter(8.0, 2.142222222222222), 0.0);
+
+ // 0xc020000000000000 0x400123456789abcd -> c01fffffffffffff
+ Assert.assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 2.142222222222222), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a224 -> 3f2e43753d36a224
+ Assert.assertEquals(2.308922399667661E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a223 -> 3f2e43753d36a223
+ Assert.assertEquals(2.3089223996676606E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a222 -> 3f2e43753d36a222
+ Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a224 -> 3f2e43753d36a222
+ Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a223 -> 3f2e43753d36a222
+ Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a222 -> 3f2e43753d36a222
+ Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a224 -> bf2e43753d36a222
+ Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a223 -> bf2e43753d36a222
+ Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a222 -> bf2e43753d36a222
+ Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a224 -> bf2e43753d36a224
+ Assert.assertEquals(-2.308922399667661E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a223 -> bf2e43753d36a223
+ Assert.assertEquals(-2.3089223996676606E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a222 -> bf2e43753d36a222
+ Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
+
+ }
+
+ @Test
+ public void testDoubleNextAfterSpecialCases() {
+ Assert.assertEquals(-Double.MAX_VALUE,FastMath.nextAfter(Double.NEGATIVE_INFINITY, 0D), 0D);
+ Assert.assertEquals(Double.MAX_VALUE,FastMath.nextAfter(Double.POSITIVE_INFINITY, 0D), 0D);
+ Assert.assertEquals(Double.NaN,FastMath.nextAfter(Double.NaN, 0D), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY,FastMath.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY,FastMath.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY), 0D);
+ Assert.assertEquals(Double.MIN_VALUE, FastMath.nextAfter(0D, 1D), 0D);
+ Assert.assertEquals(-Double.MIN_VALUE, FastMath.nextAfter(0D, -1D), 0D);
+ Assert.assertEquals(0D, FastMath.nextAfter(Double.MIN_VALUE, -1), 0D);
+ Assert.assertEquals(0D, FastMath.nextAfter(-Double.MIN_VALUE, 1), 0D);
+ }
+
+ @Test
+ public void testFloatNextAfterSpecialCases() {
+ Assert.assertEquals(-Float.MAX_VALUE,FastMath.nextAfter(Float.NEGATIVE_INFINITY, 0F), 0F);
+ Assert.assertEquals(Float.MAX_VALUE,FastMath.nextAfter(Float.POSITIVE_INFINITY, 0F), 0F);
+ Assert.assertEquals(Float.NaN,FastMath.nextAfter(Float.NaN, 0F), 0F);
+ Assert.assertEquals(Float.POSITIVE_INFINITY,FastMath.nextAfter(Float.MAX_VALUE, Float.POSITIVE_INFINITY), 0F);
+ Assert.assertEquals(Float.NEGATIVE_INFINITY,FastMath.nextAfter(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY), 0F);
+ Assert.assertEquals(Float.MIN_VALUE, FastMath.nextAfter(0F, 1F), 0F);
+ Assert.assertEquals(-Float.MIN_VALUE, FastMath.nextAfter(0F, -1F), 0F);
+ Assert.assertEquals(0F, FastMath.nextAfter(Float.MIN_VALUE, -1F), 0F);
+ Assert.assertEquals(0F, FastMath.nextAfter(-Float.MIN_VALUE, 1F), 0F);
+ }
+
+ @Test
+ public void testDoubleScalbSpecialCases() {
+ Assert.assertEquals(2.5269841324701218E-175, FastMath.scalb(2.2250738585072014E-308, 442), 0D);
+ Assert.assertEquals(1.307993905256674E297, FastMath.scalb(1.1102230246251565E-16, 1040), 0D);
+ Assert.assertEquals(7.2520887996488946E-217, FastMath.scalb(Double.MIN_VALUE, 356), 0D);
+ Assert.assertEquals(8.98846567431158E307, FastMath.scalb(Double.MIN_VALUE, 2097), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb(Double.MIN_VALUE, 2098), 0D);
+ Assert.assertEquals(1.1125369292536007E-308, FastMath.scalb(2.225073858507201E-308, -1), 0D);
+ Assert.assertEquals(1.0E-323, FastMath.scalb(Double.MAX_VALUE, -2097), 0D);
+ Assert.assertEquals(Double.MIN_VALUE, FastMath.scalb(Double.MAX_VALUE, -2098), 0D);
+ Assert.assertEquals(0, FastMath.scalb(Double.MAX_VALUE, -2099), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb(Double.POSITIVE_INFINITY, -1000000), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.1102230246251565E-16, 1078), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.1102230246251565E-16, 1079), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-2.2250738585072014E-308, 2047), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-2.2250738585072014E-308, 2048), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.7976931348623157E308, 2147483647), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb( 1.7976931348623157E308, 2147483647), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.1102230246251565E-16, 2147483647), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb( 1.1102230246251565E-16, 2147483647), 0D);
+ Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-2.2250738585072014E-308, 2147483647), 0D);
+ Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb( 2.2250738585072014E-308, 2147483647), 0D);
+ }
+
+ @Test
+ public void testFloatScalbSpecialCases() {
+ Assert.assertEquals(0f, FastMath.scalb(Float.MIN_VALUE, -30), 0F);
+ Assert.assertEquals(2 * Float.MIN_VALUE, FastMath.scalb(Float.MIN_VALUE, 1), 0F);
+ Assert.assertEquals(7.555786e22f, FastMath.scalb(Float.MAX_VALUE, -52), 0F);
+ Assert.assertEquals(1.7014118e38f, FastMath.scalb(Float.MIN_VALUE, 276), 0F);
+ Assert.assertEquals(Float.POSITIVE_INFINITY, FastMath.scalb(Float.MIN_VALUE, 277), 0F);
+ Assert.assertEquals(5.8774718e-39f, FastMath.scalb(1.1754944e-38f, -1), 0F);
+ Assert.assertEquals(2 * Float.MIN_VALUE, FastMath.scalb(Float.MAX_VALUE, -276), 0F);
+ Assert.assertEquals(Float.MIN_VALUE, FastMath.scalb(Float.MAX_VALUE, -277), 0F);
+ Assert.assertEquals(0, FastMath.scalb(Float.MAX_VALUE, -278), 0F);
+ Assert.assertEquals(Float.POSITIVE_INFINITY, FastMath.scalb(Float.POSITIVE_INFINITY, -1000000), 0F);
+ Assert.assertEquals(-3.13994498e38f, FastMath.scalb(-1.1e-7f, 151), 0F);
+ Assert.assertEquals(Float.NEGATIVE_INFINITY, FastMath.scalb(-1.1e-7f, 152), 0F);
+ Assert.assertEquals(Float.POSITIVE_INFINITY, FastMath.scalb(3.4028235E38f, 2147483647), 0F);
+ Assert.assertEquals(Float.NEGATIVE_INFINITY, FastMath.scalb(-3.4028235E38f, 2147483647), 0F);
+ }
+
+ private boolean compareClassMethods(Class<?> class1, Class<?> class2){
+ boolean allfound = true;
+ for(Method method1 : class1.getDeclaredMethods()){
+ if (Modifier.isPublic(method1.getModifiers())){
+ Type []params = method1.getGenericParameterTypes();
+ try {
+ class2.getDeclaredMethod(method1.getName(), (Class[]) params);
+ } catch (NoSuchMethodException e) {
+ allfound = false;
+ System.out.println(class2.getSimpleName()+" does not implement: "+method1);
+ }
+ }
+ }
+ return allfound;
+ }
+
+ @Test
+ public void checkMissingFastMathClasses() {
+ boolean ok = compareClassMethods(StrictMath.class, FastMath.class);
+ Assert.assertTrue("FastMath should implement all StrictMath methods", ok);
+ }
+
+ @Ignore
+ @Test
+ public void checkExtraFastMathClasses() {
+ compareClassMethods( FastMath.class, StrictMath.class);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/FastMathTestPerformance.java b/src/test/java/org/apache/commons/math/util/FastMathTestPerformance.java
new file mode 100644
index 0000000..fe4b1ac
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/FastMathTestPerformance.java
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Performance tests for FastMath.
+ * Not enabled by default, as the class does not end in Test.
+ *
+ * Invoke by running<br/>
+ * {@code mvn test -Dtest=FastMathTestPerformance}<br/>
+ * or by running<br/>
+ * {@code mvn test -Dtest=FastMathTestPerformance -DargLine="-DtestRuns=1234 -server"}<br/>
+ */
+public class FastMathTestPerformance {
+ private static final int RUNS = Integer.parseInt(System.getProperty("testRuns","10000000"));
+
+ // Header format
+ private static final String FMT_HDR = "%-5s %13s %13s %13s Runs=%d Java %s (%s) %s (%s)";
+ // Detail format
+ private static final String FMT_DTL = "%-5s %6d %6.2f %6d %6.2f %6d %6.2f";
+
+ @BeforeClass
+ public static void header() {
+ System.out.println(String.format(FMT_HDR,
+ "Name","StrictMath","FastMath","Math",RUNS,
+ System.getProperty("java.version"),
+ System.getProperty("java.runtime.version","?"),
+ System.getProperty("java.vm.name"),
+ System.getProperty("java.vm.version")
+ ));
+ }
+
+ private static void report(String name, long strictMathTime, long fastMathTime, long mathTime) {
+ long unitTime = strictMathTime;
+ System.out.println(String.format(FMT_DTL,
+ name,
+ strictMathTime / RUNS, (double) strictMathTime / unitTime,
+ fastMathTime / RUNS, (double) fastMathTime / unitTime,
+ mathTime / RUNS, (double) mathTime / unitTime
+ ));
+ }
+ @Test
+ public void testLog() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.log(Math.PI + i/* 1.0 + i/1e9 */);
+ long strictMath = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.log(Math.PI + i/* 1.0 + i/1e9 */);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.log(Math.PI + i/* 1.0 + i/1e9 */);
+ long mathTime = System.nanoTime() - time;
+
+ report("log",strictMath,fastTime,mathTime);
+ }
+
+ @Test
+ public void testPow() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.pow(Math.PI + i / 1e6, i / 1e6);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.pow(Math.PI + i / 1e6, i / 1e6);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.pow(Math.PI + i / 1e6, i / 1e6);
+ long mathTime = System.nanoTime() - time;
+ report("pow",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testExp() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.exp(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.exp(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.exp(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("exp",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testSin() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.sin(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.sin(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.sin(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("sin",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testAsin() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.asin(i / 10000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.asin(i / 10000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.asin(i / 10000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("asin",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testCos() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.cos(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.cos(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.cos(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("cos",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testAcos() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.acos(i / 10000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.acos(i / 10000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.acos(i / 10000000.0);
+ long mathTime = System.nanoTime() - time;
+ report("acos",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testTan() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.tan(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.tan(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.tan(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("tan",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testAtan() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.atan(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.atan(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.atan(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("atan",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testCbrt() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.cbrt(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.cbrt(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.cbrt(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("cbrt",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testCosh() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.cosh(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.cosh(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.cosh(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("cosh",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testSinh() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.sinh(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.sinh(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.sinh(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("sinh",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testTanh() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.tanh(i / 1000000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.tanh(i / 1000000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.tanh(i / 1000000.0);
+ long mathTime = System.nanoTime() - time;
+
+ report("tanh",strictTime,fastTime,mathTime);
+ }
+
+ @Test
+ public void testExpm1() {
+ double x = 0;
+ long time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += StrictMath.expm1(-i / 100000.0);
+ long strictTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += FastMath.expm1(-i / 100000.0);
+ long fastTime = System.nanoTime() - time;
+
+ x = 0;
+ time = System.nanoTime();
+ for (int i = 0; i < RUNS; i++)
+ x += Math.expm1(-i / 100000.0);
+ long mathTime = System.nanoTime() - time;
+ report("expm1",strictTime,fastTime,mathTime);
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/MathUtilsTest.java b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
new file mode 100644
index 0000000..30fe299
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
@@ -0,0 +1,1640 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
+ * or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.math.TestUtils;
+import org.apache.commons.math.exception.NonMonotonousSequenceException;
+import org.apache.commons.math.random.RandomDataImpl;
+
+/**
+ * Test cases for the MathUtils class.
+ * @version $Revision: 1070122 $ $Date: 2007-08-16 15:36:33 -0500 (Thu, 16 Aug
+ * 2007) $
+ */
+public final class MathUtilsTest extends TestCase {
+
+ public MathUtilsTest(String name) {
+ super(name);
+ }
+
+ /** cached binomial coefficients */
+ private static final List<Map<Integer, Long>> binomialCache = new ArrayList<Map<Integer, Long>>();
+
+ /**
+ * Exact (caching) recursive implementation to test against
+ */
+ private long binomialCoefficient(int n, int k) throws ArithmeticException {
+ if (binomialCache.size() > n) {
+ Long cachedResult = binomialCache.get(n).get(Integer.valueOf(k));
+ if (cachedResult != null) {
+ return cachedResult.longValue();
+ }
+ }
+ long result = -1;
+ if ((n == k) || (k == 0)) {
+ result = 1;
+ } else if ((k == 1) || (k == n - 1)) {
+ result = n;
+ } else {
+ // Reduce stack depth for larger values of n
+ if (k < n - 100) {
+ binomialCoefficient(n - 100, k);
+ }
+ if (k > 100) {
+ binomialCoefficient(n - 100, k - 100);
+ }
+ result = MathUtils.addAndCheck(binomialCoefficient(n - 1, k - 1),
+ binomialCoefficient(n - 1, k));
+ }
+ if (result == -1) {
+ throw new ArithmeticException(
+ "error computing binomial coefficient");
+ }
+ for (int i = binomialCache.size(); i < n + 1; i++) {
+ binomialCache.add(new HashMap<Integer, Long>());
+ }
+ binomialCache.get(n).put(Integer.valueOf(k), Long.valueOf(result));
+ return result;
+ }
+
+ /**
+ * Exact direct multiplication implementation to test against
+ */
+ private long factorial(int n) {
+ long result = 1;
+ for (int i = 2; i <= n; i++) {
+ result *= i;
+ }
+ return result;
+ }
+
+ /** Verify that b(0,0) = 1 */
+ public void test0Choose0() {
+ assertEquals(MathUtils.binomialCoefficientDouble(0, 0), 1d, 0);
+ assertEquals(MathUtils.binomialCoefficientLog(0, 0), 0d, 0);
+ assertEquals(MathUtils.binomialCoefficient(0, 0), 1);
+ }
+
+ public void testAddAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ assertEquals(big, MathUtils.addAndCheck(big, 0));
+ try {
+ MathUtils.addAndCheck(big, 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ MathUtils.addAndCheck(bigNeg, -1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ public void testAddAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ assertEquals(max, MathUtils.addAndCheck(max, 0L));
+ assertEquals(min, MathUtils.addAndCheck(min, 0L));
+ assertEquals(max, MathUtils.addAndCheck(0L, max));
+ assertEquals(min, MathUtils.addAndCheck(0L, min));
+ assertEquals(1, MathUtils.addAndCheck(-1L, 2L));
+ assertEquals(1, MathUtils.addAndCheck(2L, -1L));
+ assertEquals(-3, MathUtils.addAndCheck(-2L, -1L));
+ assertEquals(min, MathUtils.addAndCheck(min + 1, -1L));
+ testAddAndCheckLongFailure(max, 1L);
+ testAddAndCheckLongFailure(min, -1L);
+ testAddAndCheckLongFailure(1L, max);
+ testAddAndCheckLongFailure(-1L, min);
+ }
+
+ private void testAddAndCheckLongFailure(long a, long b) {
+ try {
+ MathUtils.addAndCheck(a, b);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ }
+
+ public void testBinomialCoefficient() {
+ long[] bcoef5 = {
+ 1,
+ 5,
+ 10,
+ 10,
+ 5,
+ 1 };
+ long[] bcoef6 = {
+ 1,
+ 6,
+ 15,
+ 20,
+ 15,
+ 6,
+ 1 };
+ for (int i = 0; i < 6; i++) {
+ assertEquals("5 choose " + i, bcoef5[i], MathUtils.binomialCoefficient(5, i));
+ }
+ for (int i = 0; i < 7; i++) {
+ assertEquals("6 choose " + i, bcoef6[i], MathUtils.binomialCoefficient(6, i));
+ }
+
+ for (int n = 1; n < 10; n++) {
+ for (int k = 0; k <= n; k++) {
+ assertEquals(n + " choose " + k, binomialCoefficient(n, k), MathUtils.binomialCoefficient(n, k));
+ assertEquals(n + " choose " + k, binomialCoefficient(n, k), MathUtils.binomialCoefficientDouble(n, k), Double.MIN_VALUE);
+ assertEquals(n + " choose " + k, FastMath.log(binomialCoefficient(n, k)), MathUtils.binomialCoefficientLog(n, k), 10E-12);
+ }
+ }
+
+ int[] n = { 34, 66, 100, 1500, 1500 };
+ int[] k = { 17, 33, 10, 1500 - 4, 4 };
+ for (int i = 0; i < n.length; i++) {
+ long expected = binomialCoefficient(n[i], k[i]);
+ assertEquals(n[i] + " choose " + k[i], expected,
+ MathUtils.binomialCoefficient(n[i], k[i]));
+ assertEquals(n[i] + " choose " + k[i], expected,
+ MathUtils.binomialCoefficientDouble(n[i], k[i]), 0.0);
+ assertEquals("log(" + n[i] + " choose " + k[i] + ")", FastMath.log(expected),
+ MathUtils.binomialCoefficientLog(n[i], k[i]), 0.0);
+ }
+ }
+
+ /**
+ * Tests correctness for large n and sharpness of upper bound in API doc
+ * JIRA: MATH-241
+ */
+ public void testBinomialCoefficientLarge() throws Exception {
+ // This tests all legal and illegal values for n <= 200.
+ for (int n = 0; n <= 200; n++) {
+ for (int k = 0; k <= n; k++) {
+ long ourResult = -1;
+ long exactResult = -1;
+ boolean shouldThrow = false;
+ boolean didThrow = false;
+ try {
+ ourResult = MathUtils.binomialCoefficient(n, k);
+ } catch (ArithmeticException ex) {
+ didThrow = true;
+ }
+ try {
+ exactResult = binomialCoefficient(n, k);
+ } catch (ArithmeticException ex) {
+ shouldThrow = true;
+ }
+ assertEquals(n + " choose " + k, exactResult, ourResult);
+ assertEquals(n + " choose " + k, shouldThrow, didThrow);
+ assertTrue(n + " choose " + k, (n > 66 || !didThrow));
+
+ if (!shouldThrow && exactResult > 1) {
+ assertEquals(n + " choose " + k, 1.,
+ MathUtils.binomialCoefficientDouble(n, k) / exactResult, 1e-10);
+ assertEquals(n + " choose " + k, 1,
+ MathUtils.binomialCoefficientLog(n, k) / FastMath.log(exactResult), 1e-10);
+ }
+ }
+ }
+
+ long ourResult = MathUtils.binomialCoefficient(300, 3);
+ long exactResult = binomialCoefficient(300, 3);
+ assertEquals(exactResult, ourResult);
+
+ ourResult = MathUtils.binomialCoefficient(700, 697);
+ exactResult = binomialCoefficient(700, 697);
+ assertEquals(exactResult, ourResult);
+
+ // This one should throw
+ try {
+ MathUtils.binomialCoefficient(700, 300);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // Expected
+ }
+
+ int n = 10000;
+ ourResult = MathUtils.binomialCoefficient(n, 3);
+ exactResult = binomialCoefficient(n, 3);
+ assertEquals(exactResult, ourResult);
+ assertEquals(1, MathUtils.binomialCoefficientDouble(n, 3) / exactResult, 1e-10);
+ assertEquals(1, MathUtils.binomialCoefficientLog(n, 3) / FastMath.log(exactResult), 1e-10);
+
+ }
+
+ public void testBinomialCoefficientFail() {
+ try {
+ MathUtils.binomialCoefficient(4, 5);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ try {
+ MathUtils.binomialCoefficientDouble(4, 5);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ try {
+ MathUtils.binomialCoefficientLog(4, 5);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ try {
+ MathUtils.binomialCoefficient(-1, -2);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.binomialCoefficientDouble(-1, -2);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.binomialCoefficientLog(-1, -2);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+
+ try {
+ MathUtils.binomialCoefficient(67, 30);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.binomialCoefficient(67, 34);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // ignored
+ }
+ double x = MathUtils.binomialCoefficientDouble(1030, 515);
+ assertTrue("expecting infinite binomial coefficient", Double
+ .isInfinite(x));
+ }
+
+ public void testCompareTo() {
+ assertEquals(0, MathUtils.compareTo(152.33, 152.32, .011));
+ assertTrue(MathUtils.compareTo(152.308, 152.32, .011) < 0);
+ assertTrue(MathUtils.compareTo(152.33, 152.318, .011) > 0);
+ }
+
+ public void testCosh() {
+ double x = 3.0;
+ double expected = 10.06766;
+ assertEquals(expected, MathUtils.cosh(x), 1.0e-5);
+ }
+
+ public void testCoshNaN() {
+ assertTrue(Double.isNaN(MathUtils.cosh(Double.NaN)));
+ }
+
+ public void testEquals() {
+ double[] testArray = {
+ Double.NaN,
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ 1d,
+ 0d };
+ for (int i = 0; i < testArray.length; i++) {
+ for (int j = 0; j < testArray.length; j++) {
+ if (i == j) {
+ assertTrue(MathUtils.equals(testArray[i], testArray[j]));
+ assertTrue(MathUtils.equals(testArray[j], testArray[i]));
+ } else {
+ assertTrue(!MathUtils.equals(testArray[i], testArray[j]));
+ assertTrue(!MathUtils.equals(testArray[j], testArray[i]));
+ }
+ }
+ }
+ }
+
+ public void testEqualsIncludingNaN() {
+ double[] testArray = {
+ Double.NaN,
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ 1d,
+ 0d };
+ for (int i = 0; i < testArray.length; i++) {
+ for (int j = 0; j < testArray.length; j++) {
+ if (i == j) {
+ assertTrue(MathUtils.equalsIncludingNaN(testArray[i], testArray[j]));
+ assertTrue(MathUtils.equalsIncludingNaN(testArray[j], testArray[i]));
+ } else {
+ assertTrue(!MathUtils.equalsIncludingNaN(testArray[i], testArray[j]));
+ assertTrue(!MathUtils.equalsIncludingNaN(testArray[j], testArray[i]));
+ }
+ }
+ }
+ }
+
+ public void testEqualsWithAllowedDelta() {
+ assertTrue(MathUtils.equals(153.0000, 153.0000, .0625));
+ assertTrue(MathUtils.equals(153.0000, 153.0625, .0625));
+ assertTrue(MathUtils.equals(152.9375, 153.0000, .0625));
+ assertTrue(MathUtils.equals(Double.NaN, Double.NaN, 1.0)); // This will change in 3.0
+ assertTrue(MathUtils.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0));
+ assertTrue(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.0));
+ assertFalse(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0));
+ assertFalse(MathUtils.equals(153.0000, 153.0625, .0624));
+ assertFalse(MathUtils.equals(152.9374, 153.0000, .0625));
+ }
+
+ public void testEqualsIncludingNaNWithAllowedDelta() {
+ assertTrue(MathUtils.equalsIncludingNaN(153.0000, 153.0000, .0625));
+ assertTrue(MathUtils.equalsIncludingNaN(153.0000, 153.0625, .0625));
+ assertTrue(MathUtils.equalsIncludingNaN(152.9375, 153.0000, .0625));
+ assertTrue(MathUtils.equalsIncludingNaN(Double.NaN, Double.NaN, 1.0));
+ assertTrue(MathUtils.equalsIncludingNaN(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0));
+ assertTrue(MathUtils.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.0));
+ assertFalse(MathUtils.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0));
+ assertFalse(MathUtils.equalsIncludingNaN(153.0000, 153.0625, .0624));
+ assertFalse(MathUtils.equalsIncludingNaN(152.9374, 153.0000, .0625));
+ }
+
+ // Tests for floating point equality
+ @SuppressWarnings("deprecation") // Math.equals(float, float)
+ public void testFloatEqualsWithAllowedUlps() {
+ assertTrue("+0.0f == -0.0f",MathUtils.equals(0.0f, -0.0f));
+ assertTrue("+0.0f == -0.0f (1 ulp)",MathUtils.equals(0.0f, -0.0f, 1));
+ float oneFloat = 1.0f;
+ // Deprecated method - requires parameters to be strictly equal
+ assertFalse("1.0f != 1.0f + 1 ulp",MathUtils.equals(oneFloat, Float.intBitsToFloat(1 + Float.floatToIntBits(oneFloat))));
+ assertTrue("1.0f == 1.0f + 1 ulp (1 ulp)",MathUtils.equals(oneFloat, Float.intBitsToFloat(1 + Float.floatToIntBits(oneFloat)), 1));
+ assertFalse("1.0f != 1.0f + 2 ulp (1 ulp)",MathUtils.equals(oneFloat, Float.intBitsToFloat(2 + Float.floatToIntBits(oneFloat)), 1));
+
+ assertTrue(MathUtils.equals(153.0f, 153.0f, 1));
+
+ // These tests need adjusting for floating point precision
+// assertTrue(MathUtils.equals(153.0f, 153.00000000000003f, 1));
+// assertFalse(MathUtils.equals(153.0f, 153.00000000000006f, 1));
+// assertTrue(MathUtils.equals(153.0f, 152.99999999999997f, 1));
+// assertFalse(MathUtils.equals(153f, 152.99999999999994f, 1));
+//
+// assertTrue(MathUtils.equals(-128.0f, -127.99999999999999f, 1));
+// assertFalse(MathUtils.equals(-128.0f, -127.99999999999997f, 1));
+// assertTrue(MathUtils.equals(-128.0f, -128.00000000000003f, 1));
+// assertFalse(MathUtils.equals(-128.0f, -128.00000000000006f, 1));
+
+ assertTrue(MathUtils.equals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(Double.MAX_VALUE, Float.POSITIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY, 1));
+
+ assertFalse(MathUtils.equals(Float.NaN, Float.NaN, 1));
+
+ assertFalse(MathUtils.equals(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 100000));
+ }
+
+ public void testEqualsWithAllowedUlps21() { // From 2.1
+ assertTrue(MathUtils.equals(153, 153, 1));
+
+ assertTrue(MathUtils.equals(153, 153.00000000000003, 1));
+ assertFalse(MathUtils.equals(153, 153.00000000000006, 1));
+ assertTrue(MathUtils.equals(153, 152.99999999999997, 1));
+ assertFalse(MathUtils.equals(153, 152.99999999999994, 1));
+
+ assertTrue(MathUtils.equals(-128, -127.99999999999999, 1));
+ assertFalse(MathUtils.equals(-128, -127.99999999999997, 1));
+ assertTrue(MathUtils.equals(-128, -128.00000000000003, 1));
+ assertFalse(MathUtils.equals(-128, -128.00000000000006, 1));
+
+ assertTrue(MathUtils.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1));
+
+
+ assertTrue(MathUtils.equals(Double.NaN, Double.NaN, 1));
+
+ assertFalse(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000));
+ }
+
+ public void testEqualsWithAllowedUlps() {
+ assertTrue(MathUtils.equals(0.0, -0.0, 1));
+
+ assertTrue(MathUtils.equals(1.0, 1 + FastMath.ulp(1d), 1));
+ assertFalse(MathUtils.equals(1.0, 1 + 2 * FastMath.ulp(1d), 1));
+
+ final double nUp1 = FastMath.nextAfter(1d, Double.POSITIVE_INFINITY);
+ final double nnUp1 = FastMath.nextAfter(nUp1, Double.POSITIVE_INFINITY);
+ assertTrue(MathUtils.equals(1.0, nUp1, 1));
+ assertTrue(MathUtils.equals(nUp1, nnUp1, 1));
+ assertFalse(MathUtils.equals(1.0, nnUp1, 1));
+
+ assertTrue(MathUtils.equals(0.0, FastMath.ulp(0d), 1));
+ assertTrue(MathUtils.equals(0.0, -FastMath.ulp(0d), 1));
+
+ assertTrue(MathUtils.equals(153.0, 153.0, 1));
+
+ assertTrue(MathUtils.equals(153.0, 153.00000000000003, 1));
+ assertFalse(MathUtils.equals(153.0, 153.00000000000006, 1));
+ assertTrue(MathUtils.equals(153.0, 152.99999999999997, 1));
+ assertFalse(MathUtils.equals(153, 152.99999999999994, 1));
+
+ assertTrue(MathUtils.equals(-128.0, -127.99999999999999, 1));
+ assertFalse(MathUtils.equals(-128.0, -127.99999999999997, 1));
+ assertTrue(MathUtils.equals(-128.0, -128.00000000000003, 1));
+ assertFalse(MathUtils.equals(-128.0, -128.00000000000006, 1));
+
+ assertTrue(MathUtils.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1));
+ assertTrue(MathUtils.equals(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equals(Double.NaN, Double.NaN, 1)); // This will change in 3.0
+
+ assertFalse(MathUtils.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000));
+ }
+
+ public void testEqualsIncludingNaNWithAllowedUlps() {
+ assertTrue(MathUtils.equalsIncludingNaN(0.0, -0.0, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(1.0, 1 + FastMath.ulp(1d), 1));
+ assertFalse(MathUtils.equalsIncludingNaN(1.0, 1 + 2 * FastMath.ulp(1d), 1));
+
+ final double nUp1 = FastMath.nextAfter(1d, Double.POSITIVE_INFINITY);
+ final double nnUp1 = FastMath.nextAfter(nUp1, Double.POSITIVE_INFINITY);
+ assertTrue(MathUtils.equalsIncludingNaN(1.0, nUp1, 1));
+ assertTrue(MathUtils.equalsIncludingNaN(nUp1, nnUp1, 1));
+ assertFalse(MathUtils.equalsIncludingNaN(1.0, nnUp1, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(0.0, FastMath.ulp(0d), 1));
+ assertTrue(MathUtils.equalsIncludingNaN(0.0, -FastMath.ulp(0d), 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(153.0, 153.0, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(153.0, 153.00000000000003, 1));
+ assertFalse(MathUtils.equalsIncludingNaN(153.0, 153.00000000000006, 1));
+ assertTrue(MathUtils.equalsIncludingNaN(153.0, 152.99999999999997, 1));
+ assertFalse(MathUtils.equalsIncludingNaN(153, 152.99999999999994, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(-128.0, -127.99999999999999, 1));
+ assertFalse(MathUtils.equalsIncludingNaN(-128.0, -127.99999999999997, 1));
+ assertTrue(MathUtils.equalsIncludingNaN(-128.0, -128.00000000000003, 1));
+ assertFalse(MathUtils.equalsIncludingNaN(-128.0, -128.00000000000006, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1));
+ assertTrue(MathUtils.equalsIncludingNaN(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1));
+ assertTrue(MathUtils.equalsIncludingNaN(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1));
+
+ assertTrue(MathUtils.equalsIncludingNaN(Double.NaN, Double.NaN, 1));
+
+ assertFalse(MathUtils.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000));
+ }
+
+ @SuppressWarnings("deprecation") // specific tests of deprecated methods
+ public void testArrayEquals() {
+ assertFalse(MathUtils.equals(new double[] { 1d }, null));
+ assertFalse(MathUtils.equals(null, new double[] { 1d }));
+ assertTrue(MathUtils.equals((double[]) null, (double[]) null));
+
+ assertFalse(MathUtils.equals(new double[] { 1d }, new double[0]));
+ assertTrue(MathUtils.equals(new double[] { 1d }, new double[] { 1d }));
+ assertTrue(MathUtils.equals(new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }, new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }));
+ assertFalse(MathUtils.equals(new double[] { Double.POSITIVE_INFINITY },
+ new double[] { Double.NEGATIVE_INFINITY }));
+ assertFalse(MathUtils.equals(new double[] { 1d },
+ new double[] { FastMath.nextAfter(1d, 2d) }));
+
+ }
+
+ public void testArrayEqualsIncludingNaN() {
+ assertFalse(MathUtils.equalsIncludingNaN(new double[] { 1d }, null));
+ assertFalse(MathUtils.equalsIncludingNaN(null, new double[] { 1d }));
+ assertTrue(MathUtils.equalsIncludingNaN((double[]) null, (double[]) null));
+
+ assertFalse(MathUtils.equalsIncludingNaN(new double[] { 1d }, new double[0]));
+ assertTrue(MathUtils.equalsIncludingNaN(new double[] { 1d }, new double[] { 1d }));
+ assertTrue(MathUtils.equalsIncludingNaN(new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }, new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }));
+ assertFalse(MathUtils.equalsIncludingNaN(new double[] { Double.POSITIVE_INFINITY },
+ new double[] { Double.NEGATIVE_INFINITY }));
+ assertFalse(MathUtils.equalsIncludingNaN(new double[] { 1d },
+ new double[] { FastMath.nextAfter(FastMath.nextAfter(1d, 2d), 2d) }));
+ }
+
+ public void testFloatArrayEqualsIncludingNaN() {
+ assertFalse(MathUtils.equalsIncludingNaN(new float[] { 1f }, null));
+ assertFalse(MathUtils.equalsIncludingNaN(null, new float[] { 1f }));
+ assertTrue(MathUtils.equalsIncludingNaN((float[]) null, (float[]) null));
+
+ assertFalse(MathUtils.equalsIncludingNaN(new float[] { 1f }, new float[0]));
+ assertTrue(MathUtils.equalsIncludingNaN(new float[] { 1f }, new float[] { 1f }));
+ assertTrue(MathUtils.equalsIncludingNaN(new float[] {
+ Float.NaN, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY, 1f, 0f
+ }, new float[] {
+ Float.NaN, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY, 1f, 0f
+ }));
+ assertFalse(MathUtils.equalsIncludingNaN(new float[] { Float.POSITIVE_INFINITY },
+ new float[] { Float.NEGATIVE_INFINITY }));
+// assertFalse(MathUtils.equalsIncludingNaN(new float[] { 1f },
+// new float[] { FastMath.nextAfter(FastMath.nextAfter(1f, 2f), 2f) }));
+ }
+
+ public void testFactorial() {
+ for (int i = 1; i < 21; i++) {
+ assertEquals(i + "! ", factorial(i), MathUtils.factorial(i));
+ assertEquals(i + "! ", factorial(i), MathUtils.factorialDouble(i), Double.MIN_VALUE);
+ assertEquals(i + "! ", FastMath.log(factorial(i)), MathUtils.factorialLog(i), 10E-12);
+ }
+
+ assertEquals("0", 1, MathUtils.factorial(0));
+ assertEquals("0", 1.0d, MathUtils.factorialDouble(0), 1E-14);
+ assertEquals("0", 0.0d, MathUtils.factorialLog(0), 1E-14);
+ }
+
+ public void testFactorialFail() {
+ try {
+ MathUtils.factorial(-1);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.factorialDouble(-1);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.factorialLog(-1);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // ignored
+ }
+ try {
+ MathUtils.factorial(21);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // ignored
+ }
+ assertTrue("expecting infinite factorial value", Double.isInfinite(MathUtils.factorialDouble(171)));
+ }
+
+ public void testGcd() {
+ int a = 30;
+ int b = 50;
+ int c = 77;
+
+ assertEquals(0, MathUtils.gcd(0, 0));
+
+ assertEquals(b, MathUtils.gcd(0, b));
+ assertEquals(a, MathUtils.gcd(a, 0));
+ assertEquals(b, MathUtils.gcd(0, -b));
+ assertEquals(a, MathUtils.gcd(-a, 0));
+
+ assertEquals(10, MathUtils.gcd(a, b));
+ assertEquals(10, MathUtils.gcd(-a, b));
+ assertEquals(10, MathUtils.gcd(a, -b));
+ assertEquals(10, MathUtils.gcd(-a, -b));
+
+ assertEquals(1, MathUtils.gcd(a, c));
+ assertEquals(1, MathUtils.gcd(-a, c));
+ assertEquals(1, MathUtils.gcd(a, -c));
+ assertEquals(1, MathUtils.gcd(-a, -c));
+
+ assertEquals(3 * (1<<15), MathUtils.gcd(3 * (1<<20), 9 * (1<<15)));
+
+ assertEquals(Integer.MAX_VALUE, MathUtils.gcd(Integer.MAX_VALUE, 0));
+ assertEquals(Integer.MAX_VALUE, MathUtils.gcd(-Integer.MAX_VALUE, 0));
+ assertEquals(1<<30, MathUtils.gcd(1<<30, -Integer.MIN_VALUE));
+ try {
+ // gcd(Integer.MIN_VALUE, 0) > Integer.MAX_VALUE
+ MathUtils.gcd(Integer.MIN_VALUE, 0);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(0, Integer.MIN_VALUE) > Integer.MAX_VALUE
+ MathUtils.gcd(0, Integer.MIN_VALUE);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(Integer.MIN_VALUE, Integer.MIN_VALUE) > Integer.MAX_VALUE
+ MathUtils.gcd(Integer.MIN_VALUE, Integer.MIN_VALUE);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ public void testGcdLong(){
+ long a = 30;
+ long b = 50;
+ long c = 77;
+
+ assertEquals(0, MathUtils.gcd(0L, 0));
+
+ assertEquals(b, MathUtils.gcd(0, b));
+ assertEquals(a, MathUtils.gcd(a, 0));
+ assertEquals(b, MathUtils.gcd(0, -b));
+ assertEquals(a, MathUtils.gcd(-a, 0));
+
+ assertEquals(10, MathUtils.gcd(a, b));
+ assertEquals(10, MathUtils.gcd(-a, b));
+ assertEquals(10, MathUtils.gcd(a, -b));
+ assertEquals(10, MathUtils.gcd(-a, -b));
+
+ assertEquals(1, MathUtils.gcd(a, c));
+ assertEquals(1, MathUtils.gcd(-a, c));
+ assertEquals(1, MathUtils.gcd(a, -c));
+ assertEquals(1, MathUtils.gcd(-a, -c));
+
+ assertEquals(3L * (1L<<45), MathUtils.gcd(3L * (1L<<50), 9L * (1L<<45)));
+
+ assertEquals(1L<<45, MathUtils.gcd(1L<<45, Long.MIN_VALUE));
+
+ assertEquals(Long.MAX_VALUE, MathUtils.gcd(Long.MAX_VALUE, 0L));
+ assertEquals(Long.MAX_VALUE, MathUtils.gcd(-Long.MAX_VALUE, 0L));
+ assertEquals(1, MathUtils.gcd(60247241209L, 153092023L));
+ try {
+ // gcd(Long.MIN_VALUE, 0) > Long.MAX_VALUE
+ MathUtils.gcd(Long.MIN_VALUE, 0);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(0, Long.MIN_VALUE) > Long.MAX_VALUE
+ MathUtils.gcd(0, Long.MIN_VALUE);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(Long.MIN_VALUE, Long.MIN_VALUE) > Long.MAX_VALUE
+ MathUtils.gcd(Long.MIN_VALUE, Long.MIN_VALUE);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ public void testGcdConsistency() {
+ int[] primeList = {19, 23, 53, 67, 73, 79, 101, 103, 111, 131};
+ ArrayList<Integer> primes = new ArrayList<Integer>();
+ for (int i = 0; i < primeList.length; i++) {
+ primes.add(Integer.valueOf(primeList[i]));
+ }
+ RandomDataImpl randomData = new RandomDataImpl();
+ for (int i = 0; i < 20; i++) {
+ Object[] sample = randomData.nextSample(primes, 4);
+ int p1 = ((Integer) sample[0]).intValue();
+ int p2 = ((Integer) sample[1]).intValue();
+ int p3 = ((Integer) sample[2]).intValue();
+ int p4 = ((Integer) sample[3]).intValue();
+ int i1 = p1 * p2 * p3;
+ int i2 = p1 * p2 * p4;
+ int gcd = p1 * p2;
+ assertEquals(gcd, MathUtils.gcd(i1, i2));
+ long l1 = i1;
+ long l2 = i2;
+ assertEquals(gcd, MathUtils.gcd(l1, l2));
+ }
+ }
+
+ public void testHash() {
+ double[] testArray = {
+ Double.NaN,
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ 1d,
+ 0d,
+ 1E-14,
+ (1 + 1E-14),
+ Double.MIN_VALUE,
+ Double.MAX_VALUE };
+ for (int i = 0; i < testArray.length; i++) {
+ for (int j = 0; j < testArray.length; j++) {
+ if (i == j) {
+ assertEquals(MathUtils.hash(testArray[i]), MathUtils.hash(testArray[j]));
+ assertEquals(MathUtils.hash(testArray[j]), MathUtils.hash(testArray[i]));
+ } else {
+ assertTrue(MathUtils.hash(testArray[i]) != MathUtils.hash(testArray[j]));
+ assertTrue(MathUtils.hash(testArray[j]) != MathUtils.hash(testArray[i]));
+ }
+ }
+ }
+ }
+
+ public void testArrayHash() {
+ assertEquals(0, MathUtils.hash((double[]) null));
+ assertEquals(MathUtils.hash(new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }),
+ MathUtils.hash(new double[] {
+ Double.NaN, Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY, 1d, 0d
+ }));
+ assertFalse(MathUtils.hash(new double[] { 1d }) ==
+ MathUtils.hash(new double[] { FastMath.nextAfter(1d, 2d) }));
+ assertFalse(MathUtils.hash(new double[] { 1d }) ==
+ MathUtils.hash(new double[] { 1d, 1d }));
+ }
+
+ /**
+ * Make sure that permuted arrays do not hash to the same value.
+ */
+ public void testPermutedArrayHash() {
+ double[] original = new double[10];
+ double[] permuted = new double[10];
+ RandomDataImpl random = new RandomDataImpl();
+
+ // Generate 10 distinct random values
+ for (int i = 0; i < 10; i++) {
+ original[i] = random.nextUniform(i + 0.5, i + 0.75);
+ }
+
+ // Generate a random permutation, making sure it is not the identity
+ boolean isIdentity = true;
+ do {
+ int[] permutation = random.nextPermutation(10, 10);
+ for (int i = 0; i < 10; i++) {
+ if (i != permutation[i]) {
+ isIdentity = false;
+ }
+ permuted[i] = original[permutation[i]];
+ }
+ } while (isIdentity);
+
+ // Verify that permuted array has different hash
+ assertFalse(MathUtils.hash(original) == MathUtils.hash(permuted));
+ }
+
+ public void testIndicatorByte() {
+ assertEquals((byte)1, MathUtils.indicator((byte)2));
+ assertEquals((byte)1, MathUtils.indicator((byte)0));
+ assertEquals((byte)(-1), MathUtils.indicator((byte)(-2)));
+ }
+
+ public void testIndicatorDouble() {
+ double delta = 0.0;
+ assertEquals(1.0, MathUtils.indicator(2.0), delta);
+ assertEquals(1.0, MathUtils.indicator(0.0), delta);
+ assertEquals(-1.0, MathUtils.indicator(-2.0), delta);
+ assertEquals(Double.NaN, MathUtils.indicator(Double.NaN));
+ }
+
+ public void testIndicatorFloat() {
+ float delta = 0.0F;
+ assertEquals(1.0F, MathUtils.indicator(2.0F), delta);
+ assertEquals(1.0F, MathUtils.indicator(0.0F), delta);
+ assertEquals(-1.0F, MathUtils.indicator(-2.0F), delta);
+ }
+
+ public void testIndicatorInt() {
+ assertEquals(1, MathUtils.indicator((2)));
+ assertEquals(1, MathUtils.indicator((0)));
+ assertEquals((-1), MathUtils.indicator((-2)));
+ }
+
+ public void testIndicatorLong() {
+ assertEquals(1L, MathUtils.indicator(2L));
+ assertEquals(1L, MathUtils.indicator(0L));
+ assertEquals(-1L, MathUtils.indicator(-2L));
+ }
+
+ public void testIndicatorShort() {
+ assertEquals((short)1, MathUtils.indicator((short)2));
+ assertEquals((short)1, MathUtils.indicator((short)0));
+ assertEquals((short)(-1), MathUtils.indicator((short)(-2)));
+ }
+
+ public void testLcm() {
+ int a = 30;
+ int b = 50;
+ int c = 77;
+
+ assertEquals(0, MathUtils.lcm(0, b));
+ assertEquals(0, MathUtils.lcm(a, 0));
+ assertEquals(b, MathUtils.lcm(1, b));
+ assertEquals(a, MathUtils.lcm(a, 1));
+ assertEquals(150, MathUtils.lcm(a, b));
+ assertEquals(150, MathUtils.lcm(-a, b));
+ assertEquals(150, MathUtils.lcm(a, -b));
+ assertEquals(150, MathUtils.lcm(-a, -b));
+ assertEquals(2310, MathUtils.lcm(a, c));
+
+ // Assert that no intermediate value overflows:
+ // The naive implementation of lcm(a,b) would be (a*b)/gcd(a,b)
+ assertEquals((1<<20)*15, MathUtils.lcm((1<<20)*3, (1<<20)*5));
+
+ // Special case
+ assertEquals(0, MathUtils.lcm(0, 0));
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ MathUtils.lcm(Integer.MIN_VALUE, 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ MathUtils.lcm(Integer.MIN_VALUE, 1<<20);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ MathUtils.lcm(Integer.MAX_VALUE, Integer.MAX_VALUE - 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ public void testLcmLong() {
+ long a = 30;
+ long b = 50;
+ long c = 77;
+
+ assertEquals(0, MathUtils.lcm(0, b));
+ assertEquals(0, MathUtils.lcm(a, 0));
+ assertEquals(b, MathUtils.lcm(1, b));
+ assertEquals(a, MathUtils.lcm(a, 1));
+ assertEquals(150, MathUtils.lcm(a, b));
+ assertEquals(150, MathUtils.lcm(-a, b));
+ assertEquals(150, MathUtils.lcm(a, -b));
+ assertEquals(150, MathUtils.lcm(-a, -b));
+ assertEquals(2310, MathUtils.lcm(a, c));
+
+ assertEquals(Long.MAX_VALUE, MathUtils.lcm(60247241209L, 153092023L));
+
+ // Assert that no intermediate value overflows:
+ // The naive implementation of lcm(a,b) would be (a*b)/gcd(a,b)
+ assertEquals((1L<<50)*15, MathUtils.lcm((1L<<45)*3, (1L<<50)*5));
+
+ // Special case
+ assertEquals(0L, MathUtils.lcm(0L, 0L));
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ MathUtils.lcm(Long.MIN_VALUE, 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ MathUtils.lcm(Long.MIN_VALUE, 1<<20);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ assertEquals((long) Integer.MAX_VALUE * (Integer.MAX_VALUE - 1),
+ MathUtils.lcm((long)Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
+ try {
+ MathUtils.lcm(Long.MAX_VALUE, Long.MAX_VALUE - 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ public void testLog() {
+ assertEquals(2.0, MathUtils.log(2, 4), 0);
+ assertEquals(3.0, MathUtils.log(2, 8), 0);
+ assertTrue(Double.isNaN(MathUtils.log(-1, 1)));
+ assertTrue(Double.isNaN(MathUtils.log(1, -1)));
+ assertTrue(Double.isNaN(MathUtils.log(0, 0)));
+ assertEquals(0, MathUtils.log(0, 10), 0);
+ assertEquals(Double.NEGATIVE_INFINITY, MathUtils.log(10, 0), 0);
+ }
+
+ public void testMulAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ assertEquals(big, MathUtils.mulAndCheck(big, 1));
+ try {
+ MathUtils.mulAndCheck(big, 2);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ MathUtils.mulAndCheck(bigNeg, 2);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ public void testMulAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ assertEquals(max, MathUtils.mulAndCheck(max, 1L));
+ assertEquals(min, MathUtils.mulAndCheck(min, 1L));
+ assertEquals(0L, MathUtils.mulAndCheck(max, 0L));
+ assertEquals(0L, MathUtils.mulAndCheck(min, 0L));
+ assertEquals(max, MathUtils.mulAndCheck(1L, max));
+ assertEquals(min, MathUtils.mulAndCheck(1L, min));
+ assertEquals(0L, MathUtils.mulAndCheck(0L, max));
+ assertEquals(0L, MathUtils.mulAndCheck(0L, min));
+ assertEquals(1L, MathUtils.mulAndCheck(-1L, -1L));
+ assertEquals(min, MathUtils.mulAndCheck(min / 2, 2));
+ testMulAndCheckLongFailure(max, 2L);
+ testMulAndCheckLongFailure(2L, max);
+ testMulAndCheckLongFailure(min, 2L);
+ testMulAndCheckLongFailure(2L, min);
+ testMulAndCheckLongFailure(min, -1L);
+ testMulAndCheckLongFailure(-1L, min);
+ }
+
+ private void testMulAndCheckLongFailure(long a, long b) {
+ try {
+ MathUtils.mulAndCheck(a, b);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ }
+
+ public void testNextAfter() { // Test from Math 2.1
+ // 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000
+ assertEquals(16.0, MathUtils.nextAfter(15.999999999999998, 34.27555555555555), 0.0);
+
+ // 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe
+ assertEquals(-15.999999999999996, MathUtils.nextAfter(-15.999999999999998, 34.27555555555555), 0.0);
+
+ // 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe
+ assertEquals(15.999999999999996, MathUtils.nextAfter(15.999999999999998, 2.142222222222222), 0.0);
+
+ // 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe
+ assertEquals(-15.999999999999996, MathUtils.nextAfter(-15.999999999999998, 2.142222222222222), 0.0);
+
+ // 0x4020000000000000 0x404123456789abcd -> 4020000000000001
+ assertEquals(8.000000000000002, MathUtils.nextAfter(8.0, 34.27555555555555), 0.0);
+
+ // 0xc020000000000000 0x404123456789abcd -> c01fffffffffffff
+ assertEquals(-7.999999999999999, MathUtils.nextAfter(-8.0, 34.27555555555555), 0.0);
+
+ // 0x4020000000000000 0x400123456789abcd -> 401fffffffffffff
+ assertEquals(7.999999999999999, MathUtils.nextAfter(8.0, 2.142222222222222), 0.0);
+
+ // 0xc020000000000000 0x400123456789abcd -> c01fffffffffffff
+ assertEquals(-7.999999999999999, MathUtils.nextAfter(-8.0, 2.142222222222222), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a224 -> 3f2e43753d36a224
+ assertEquals(2.308922399667661E-4, MathUtils.nextAfter(2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a223 -> 3f2e43753d36a224
+ assertEquals(2.308922399667661E-4, MathUtils.nextAfter(2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0x3f2e43753d36a222 -> 3f2e43753d36a222
+ assertEquals(2.3089223996676603E-4, MathUtils.nextAfter(2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a224 -> 3f2e43753d36a222
+ assertEquals(2.3089223996676603E-4, MathUtils.nextAfter(2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a223 -> 3f2e43753d36a222
+ assertEquals(2.3089223996676603E-4, MathUtils.nextAfter(2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
+
+ // 0x3f2e43753d36a223 0xbf2e43753d36a222 -> 3f2e43753d36a222
+ assertEquals(2.3089223996676603E-4, MathUtils.nextAfter(2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a224 -> bf2e43753d36a222
+ assertEquals(-2.3089223996676603E-4, MathUtils.nextAfter(-2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a223 -> bf2e43753d36a222
+ assertEquals(-2.3089223996676603E-4, MathUtils.nextAfter(-2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0x3f2e43753d36a222 -> bf2e43753d36a222
+ assertEquals(-2.3089223996676603E-4, MathUtils.nextAfter(-2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a224 -> bf2e43753d36a224
+ assertEquals(-2.308922399667661E-4, MathUtils.nextAfter(-2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a223 -> bf2e43753d36a224
+ assertEquals(-2.308922399667661E-4, MathUtils.nextAfter(-2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
+
+ // 0xbf2e43753d36a223 0xbf2e43753d36a222 -> bf2e43753d36a222
+ assertEquals(-2.3089223996676603E-4, MathUtils.nextAfter(-2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
+
+ }
+
+ public void testNextAfterSpecialCases() {
+ assertTrue(Double.isInfinite(MathUtils.nextAfter(Double.NEGATIVE_INFINITY, 0)));
+ assertTrue(Double.isInfinite(MathUtils.nextAfter(Double.POSITIVE_INFINITY, 0)));
+ assertTrue(Double.isNaN(MathUtils.nextAfter(Double.NaN, 0)));
+ assertTrue(Double.isInfinite(MathUtils.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY)));
+ assertTrue(Double.isInfinite(MathUtils.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY)));
+ assertEquals(Double.MIN_VALUE, MathUtils.nextAfter(0, 1), 0);
+ assertEquals(-Double.MIN_VALUE, MathUtils.nextAfter(0, -1), 0);
+ assertEquals(0, MathUtils.nextAfter(Double.MIN_VALUE, -1), 0);
+ assertEquals(0, MathUtils.nextAfter(-Double.MIN_VALUE, 1), 0);
+ }
+
+ public void testScalb() { // Math 2.1
+ assertEquals( 0.0, MathUtils.scalb(0.0, 5), 1.0e-15);
+ assertEquals(32.0, MathUtils.scalb(1.0, 5), 1.0e-15);
+ assertEquals(1.0 / 32.0, MathUtils.scalb(1.0, -5), 1.0e-15);
+ assertEquals(Math.PI, MathUtils.scalb(Math.PI, 0), 1.0e-15);
+ assertTrue(Double.isInfinite(MathUtils.scalb(Double.POSITIVE_INFINITY, 1)));
+ assertTrue(Double.isInfinite(MathUtils.scalb(Double.NEGATIVE_INFINITY, 1)));
+ assertTrue(Double.isNaN(MathUtils.scalb(Double.NaN, 1)));
+ }
+
+ public void testNormalizeAngle() {
+ for (double a = -15.0; a <= 15.0; a += 0.1) {
+ for (double b = -15.0; b <= 15.0; b += 0.2) {
+ double c = MathUtils.normalizeAngle(a, b);
+ assertTrue((b - FastMath.PI) <= c);
+ assertTrue(c <= (b + FastMath.PI));
+ double twoK = FastMath.rint((a - c) / FastMath.PI);
+ assertEquals(c, a - twoK * FastMath.PI, 1.0e-14);
+ }
+ }
+ }
+
+ public void testNormalizeArray() {
+ double[] testValues1 = new double[] {1, 1, 2};
+ TestUtils.assertEquals(
+ new double[] {.25, .25, .5},
+ MathUtils.normalizeArray(testValues1, 1),
+ Double.MIN_VALUE);
+
+ double[] testValues2 = new double[] {-1, -1, 1};
+ TestUtils.assertEquals(
+ new double[] {1, 1, -1},
+ MathUtils.normalizeArray(testValues2, 1),
+ Double.MIN_VALUE);
+
+ // Ignore NaNs
+ double[] testValues3 = new double[] {-1, -1, Double.NaN, 1, Double.NaN};
+ TestUtils.assertEquals(
+ new double[] {1, 1,Double.NaN, -1, Double.NaN},
+ MathUtils.normalizeArray(testValues3, 1),
+ Double.MIN_VALUE);
+
+ // Zero sum -> ArithmeticException
+ double[] zeroSum = new double[] {-1, 1};
+ try {
+ MathUtils.normalizeArray(zeroSum, 1);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ // Infinite elements -> ArithmeticException
+ double[] hasInf = new double[] {1, 2, 1, Double.NEGATIVE_INFINITY};
+ try {
+ MathUtils.normalizeArray(hasInf, 1);
+ fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ // Infinite target -> IllegalArgumentException
+ try {
+ MathUtils.normalizeArray(testValues1, Double.POSITIVE_INFINITY);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ // NaN target -> IllegalArgumentException
+ try {
+ MathUtils.normalizeArray(testValues1, Double.NaN);
+ fail("expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {}
+
+ }
+
+ public void testRoundDouble() {
+ double x = 1.234567890;
+ assertEquals(1.23, MathUtils.round(x, 2), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4), 0.0);
+
+ // JIRA MATH-151
+ assertEquals(39.25, MathUtils.round(39.245, 2), 0.0);
+ assertEquals(39.24, MathUtils.round(39.245, 2, BigDecimal.ROUND_DOWN), 0.0);
+ double xx = 39.0;
+ xx = xx + 245d / 1000d;
+ assertEquals(39.25, MathUtils.round(xx, 2), 0.0);
+
+ // BZ 35904
+ assertEquals(30.1d, MathUtils.round(30.095d, 2), 0.0d);
+ assertEquals(30.1d, MathUtils.round(30.095d, 1), 0.0d);
+ assertEquals(33.1d, MathUtils.round(33.095d, 1), 0.0d);
+ assertEquals(33.1d, MathUtils.round(33.095d, 2), 0.0d);
+ assertEquals(50.09d, MathUtils.round(50.085d, 2), 0.0d);
+ assertEquals(50.19d, MathUtils.round(50.185d, 2), 0.0d);
+ assertEquals(50.01d, MathUtils.round(50.005d, 2), 0.0d);
+ assertEquals(30.01d, MathUtils.round(30.005d, 2), 0.0d);
+ assertEquals(30.65d, MathUtils.round(30.645d, 2), 0.0d);
+
+ assertEquals(1.24, MathUtils.round(x, 2, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.23, MathUtils.round(-x, 2, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.234, MathUtils.round(-x, 3, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.2345, MathUtils.round(-x, 4, BigDecimal.ROUND_CEILING), 0.0);
+
+ assertEquals(1.23, MathUtils.round(x, 2, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(1.234, MathUtils.round(x, 3, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(1.2345, MathUtils.round(x, 4, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.23, MathUtils.round(-x, 2, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.234, MathUtils.round(-x, 3, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.2345, MathUtils.round(-x, 4, BigDecimal.ROUND_DOWN), 0.0);
+
+ assertEquals(1.23, MathUtils.round(x, 2, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(1.234, MathUtils.round(x, 3, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(1.2345, MathUtils.round(x, 4, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.24, MathUtils.round(-x, 2, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.235, MathUtils.round(-x, 3, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.2346, MathUtils.round(-x, 4, BigDecimal.ROUND_FLOOR), 0.0);
+
+ assertEquals(1.23, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.23, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.235, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.2346, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.234, MathUtils.round(1.2345, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.234, MathUtils.round(-1.2345, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+
+ assertEquals(1.23, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.23, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.235, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.2346, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.234, MathUtils.round(1.2345, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.234, MathUtils.round(-1.2345, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.236, MathUtils.round(1.2355, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.236, MathUtils.round(-1.2355, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+
+ assertEquals(1.23, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.23, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.235, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.2346, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.235, MathUtils.round(1.2345, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.235, MathUtils.round(-1.2345, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+
+ assertEquals(-1.23, MathUtils.round(-1.23, 2, BigDecimal.ROUND_UNNECESSARY), 0.0);
+ assertEquals(1.23, MathUtils.round(1.23, 2, BigDecimal.ROUND_UNNECESSARY), 0.0);
+
+ try {
+ MathUtils.round(1.234, 2, BigDecimal.ROUND_UNNECESSARY);
+ fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+
+ assertEquals(1.24, MathUtils.round(x, 2, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(1.235, MathUtils.round(x, 3, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(1.2346, MathUtils.round(x, 4, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.24, MathUtils.round(-x, 2, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.235, MathUtils.round(-x, 3, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.2346, MathUtils.round(-x, 4, BigDecimal.ROUND_UP), 0.0);
+
+ try {
+ MathUtils.round(1.234, 2, 1923);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // MATH-151
+ assertEquals(39.25, MathUtils.round(39.245, 2, BigDecimal.ROUND_HALF_UP), 0.0);
+
+ // special values
+ TestUtils.assertEquals(Double.NaN, MathUtils.round(Double.NaN, 2), 0.0);
+ assertEquals(0.0, MathUtils.round(0.0, 2), 0.0);
+ assertEquals(Double.POSITIVE_INFINITY, MathUtils.round(Double.POSITIVE_INFINITY, 2), 0.0);
+ assertEquals(Double.NEGATIVE_INFINITY, MathUtils.round(Double.NEGATIVE_INFINITY, 2), 0.0);
+ }
+
+ public void testRoundFloat() {
+ float x = 1.234567890f;
+ assertEquals(1.23f, MathUtils.round(x, 2), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4), 0.0);
+
+ // BZ 35904
+ assertEquals(30.1f, MathUtils.round(30.095f, 2), 0.0f);
+ assertEquals(30.1f, MathUtils.round(30.095f, 1), 0.0f);
+ assertEquals(50.09f, MathUtils.round(50.085f, 2), 0.0f);
+ assertEquals(50.19f, MathUtils.round(50.185f, 2), 0.0f);
+ assertEquals(50.01f, MathUtils.round(50.005f, 2), 0.0f);
+ assertEquals(30.01f, MathUtils.round(30.005f, 2), 0.0f);
+ assertEquals(30.65f, MathUtils.round(30.645f, 2), 0.0f);
+
+ assertEquals(1.24f, MathUtils.round(x, 2, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.23f, MathUtils.round(-x, 2, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.234f, MathUtils.round(-x, 3, BigDecimal.ROUND_CEILING), 0.0);
+ assertEquals(-1.2345f, MathUtils.round(-x, 4, BigDecimal.ROUND_CEILING), 0.0);
+
+ assertEquals(1.23f, MathUtils.round(x, 2, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(1.234f, MathUtils.round(x, 3, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(1.2345f, MathUtils.round(x, 4, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.23f, MathUtils.round(-x, 2, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.234f, MathUtils.round(-x, 3, BigDecimal.ROUND_DOWN), 0.0);
+ assertEquals(-1.2345f, MathUtils.round(-x, 4, BigDecimal.ROUND_DOWN), 0.0);
+
+ assertEquals(1.23f, MathUtils.round(x, 2, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(1.234f, MathUtils.round(x, 3, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(1.2345f, MathUtils.round(x, 4, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.24f, MathUtils.round(-x, 2, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-x, 3, BigDecimal.ROUND_FLOOR), 0.0);
+ assertEquals(-1.2346f, MathUtils.round(-x, 4, BigDecimal.ROUND_FLOOR), 0.0);
+
+ assertEquals(1.23f, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.23f, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.2346f, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(1.234f, MathUtils.round(1.2345f, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+ assertEquals(-1.234f, MathUtils.round(-1.2345f, 3, BigDecimal.ROUND_HALF_DOWN), 0.0);
+
+ assertEquals(1.23f, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.23f, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.2346f, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.234f, MathUtils.round(1.2345f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.234f, MathUtils.round(-1.2345f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(1.236f, MathUtils.round(1.2355f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+ assertEquals(-1.236f, MathUtils.round(-1.2355f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0);
+
+ assertEquals(1.23f, MathUtils.round(x, 2, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.23f, MathUtils.round(-x, 2, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-x, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.2346f, MathUtils.round(-x, 4, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(1.235f, MathUtils.round(1.2345f, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-1.2345f, 3, BigDecimal.ROUND_HALF_UP), 0.0);
+
+ assertEquals(-1.23f, MathUtils.round(-1.23f, 2, BigDecimal.ROUND_UNNECESSARY), 0.0);
+ assertEquals(1.23f, MathUtils.round(1.23f, 2, BigDecimal.ROUND_UNNECESSARY), 0.0);
+
+ try {
+ MathUtils.round(1.234f, 2, BigDecimal.ROUND_UNNECESSARY);
+ fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+
+ assertEquals(1.24f, MathUtils.round(x, 2, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(1.235f, MathUtils.round(x, 3, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(1.2346f, MathUtils.round(x, 4, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.24f, MathUtils.round(-x, 2, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.235f, MathUtils.round(-x, 3, BigDecimal.ROUND_UP), 0.0);
+ assertEquals(-1.2346f, MathUtils.round(-x, 4, BigDecimal.ROUND_UP), 0.0);
+
+ try {
+ MathUtils.round(1.234f, 2, 1923);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // success
+ }
+
+ // special values
+ TestUtils.assertEquals(Float.NaN, MathUtils.round(Float.NaN, 2), 0.0f);
+ assertEquals(0.0f, MathUtils.round(0.0f, 2), 0.0f);
+ assertEquals(Float.POSITIVE_INFINITY, MathUtils.round(Float.POSITIVE_INFINITY, 2), 0.0f);
+ assertEquals(Float.NEGATIVE_INFINITY, MathUtils.round(Float.NEGATIVE_INFINITY, 2), 0.0f);
+ }
+
+ public void testSignByte() {
+ assertEquals((byte) 1, MathUtils.sign((byte) 2));
+ assertEquals((byte) 0, MathUtils.sign((byte) 0));
+ assertEquals((byte) (-1), MathUtils.sign((byte) (-2)));
+ }
+
+ public void testSignDouble() {
+ double delta = 0.0;
+ assertEquals(1.0, MathUtils.sign(2.0), delta);
+ assertEquals(0.0, MathUtils.sign(0.0), delta);
+ assertEquals(-1.0, MathUtils.sign(-2.0), delta);
+ TestUtils.assertSame(-0. / 0., MathUtils.sign(Double.NaN));
+ }
+
+ public void testSignFloat() {
+ float delta = 0.0F;
+ assertEquals(1.0F, MathUtils.sign(2.0F), delta);
+ assertEquals(0.0F, MathUtils.sign(0.0F), delta);
+ assertEquals(-1.0F, MathUtils.sign(-2.0F), delta);
+ TestUtils.assertSame(Float.NaN, MathUtils.sign(Float.NaN));
+ }
+
+ public void testSignInt() {
+ assertEquals(1, MathUtils.sign(2));
+ assertEquals(0, MathUtils.sign(0));
+ assertEquals((-1), MathUtils.sign((-2)));
+ }
+
+ public void testSignLong() {
+ assertEquals(1L, MathUtils.sign(2L));
+ assertEquals(0L, MathUtils.sign(0L));
+ assertEquals(-1L, MathUtils.sign(-2L));
+ }
+
+ public void testSignShort() {
+ assertEquals((short) 1, MathUtils.sign((short) 2));
+ assertEquals((short) 0, MathUtils.sign((short) 0));
+ assertEquals((short) (-1), MathUtils.sign((short) (-2)));
+ }
+
+ public void testSinh() {
+ double x = 3.0;
+ double expected = 10.01787;
+ assertEquals(expected, MathUtils.sinh(x), 1.0e-5);
+ }
+
+ public void testSinhNaN() {
+ assertTrue(Double.isNaN(MathUtils.sinh(Double.NaN)));
+ }
+
+ public void testSubAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ assertEquals(big, MathUtils.subAndCheck(big, 0));
+ assertEquals(bigNeg + 1, MathUtils.subAndCheck(bigNeg, -1));
+ assertEquals(-1, MathUtils.subAndCheck(bigNeg, -big));
+ try {
+ MathUtils.subAndCheck(big, -1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ MathUtils.subAndCheck(bigNeg, 1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ public void testSubAndCheckErrorMessage() {
+ int big = Integer.MAX_VALUE;
+ try {
+ MathUtils.subAndCheck(big, -1);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ assertTrue(ex.getMessage().length() > 1);
+ }
+ }
+
+ public void testSubAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ assertEquals(max, MathUtils.subAndCheck(max, 0));
+ assertEquals(min, MathUtils.subAndCheck(min, 0));
+ assertEquals(-max, MathUtils.subAndCheck(0, max));
+ assertEquals(min + 1, MathUtils.subAndCheck(min, -1));
+ // min == -1-max
+ assertEquals(-1, MathUtils.subAndCheck(-max - 1, -max));
+ assertEquals(max, MathUtils.subAndCheck(-1, -1 - max));
+ testSubAndCheckLongFailure(0L, min);
+ testSubAndCheckLongFailure(max, -1L);
+ testSubAndCheckLongFailure(min, 1L);
+ }
+
+ private void testSubAndCheckLongFailure(long a, long b) {
+ try {
+ MathUtils.subAndCheck(a, b);
+ fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+
+ }
+
+ public void testPow() {
+
+ assertEquals(1801088541, MathUtils.pow(21, 7));
+ assertEquals(1, MathUtils.pow(21, 0));
+ try {
+ MathUtils.pow(21, -7);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ assertEquals(1801088541, MathUtils.pow(21, 7l));
+ assertEquals(1, MathUtils.pow(21, 0l));
+ try {
+ MathUtils.pow(21, -7l);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ assertEquals(1801088541l, MathUtils.pow(21l, 7));
+ assertEquals(1l, MathUtils.pow(21l, 0));
+ try {
+ MathUtils.pow(21l, -7);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ assertEquals(1801088541l, MathUtils.pow(21l, 7l));
+ assertEquals(1l, MathUtils.pow(21l, 0l));
+ try {
+ MathUtils.pow(21l, -7l);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ BigInteger twentyOne = BigInteger.valueOf(21l);
+ assertEquals(BigInteger.valueOf(1801088541l), MathUtils.pow(twentyOne, 7));
+ assertEquals(BigInteger.ONE, MathUtils.pow(twentyOne, 0));
+ try {
+ MathUtils.pow(twentyOne, -7);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ assertEquals(BigInteger.valueOf(1801088541l), MathUtils.pow(twentyOne, 7l));
+ assertEquals(BigInteger.ONE, MathUtils.pow(twentyOne, 0l));
+ try {
+ MathUtils.pow(twentyOne, -7l);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ assertEquals(BigInteger.valueOf(1801088541l), MathUtils.pow(twentyOne, BigInteger.valueOf(7l)));
+ assertEquals(BigInteger.ONE, MathUtils.pow(twentyOne, BigInteger.ZERO));
+ try {
+ MathUtils.pow(twentyOne, BigInteger.valueOf(-7l));
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ BigInteger bigOne =
+ new BigInteger("1543786922199448028351389769265814882661837148" +
+ "4763915343722775611762713982220306372888519211" +
+ "560905579993523402015636025177602059044911261");
+ assertEquals(bigOne, MathUtils.pow(twentyOne, 103));
+ assertEquals(bigOne, MathUtils.pow(twentyOne, 103l));
+ assertEquals(bigOne, MathUtils.pow(twentyOne, BigInteger.valueOf(103l)));
+
+ }
+
+ public void testL1DistanceDouble() {
+ double[] p1 = { 2.5, 0.0 };
+ double[] p2 = { -0.5, 4.0 };
+ assertEquals(7.0, MathUtils.distance1(p1, p2));
+ }
+
+ public void testL1DistanceInt() {
+ int[] p1 = { 3, 0 };
+ int[] p2 = { 0, 4 };
+ assertEquals(7, MathUtils.distance1(p1, p2));
+ }
+
+ public void testL2DistanceDouble() {
+ double[] p1 = { 2.5, 0.0 };
+ double[] p2 = { -0.5, 4.0 };
+ assertEquals(5.0, MathUtils.distance(p1, p2));
+ }
+
+ public void testL2DistanceInt() {
+ int[] p1 = { 3, 0 };
+ int[] p2 = { 0, 4 };
+ assertEquals(5.0, MathUtils.distance(p1, p2));
+ }
+
+ public void testLInfDistanceDouble() {
+ double[] p1 = { 2.5, 0.0 };
+ double[] p2 = { -0.5, 4.0 };
+ assertEquals(4.0, MathUtils.distanceInf(p1, p2));
+ }
+
+ public void testLInfDistanceInt() {
+ int[] p1 = { 3, 0 };
+ int[] p2 = { 0, 4 };
+ assertEquals(4, MathUtils.distanceInf(p1, p2));
+ }
+
+ public void testCheckOrder21() { // Test from 2.1
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, 2, 15}, 1, true);
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, 2, 2}, 1, false);
+ MathUtils.checkOrder(new double[] {3, -5.5, -11, -27.5}, -1, true);
+ MathUtils.checkOrder(new double[] {3, 0, 0, -5.5, -11, -27.5}, -1, false);
+
+ try {
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, -1, 2, 15}, 1, true);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, -2, 2}, 1, false);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {3, 3, -5.5, -11, -27.5}, -1, true);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {3, -1, 0, -5.5, -11, -27.5}, -1, false);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ }
+
+ public void testCheckOrder() {
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, 2, 15},
+ MathUtils.OrderDirection.INCREASING, true);
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, 2, 2},
+ MathUtils.OrderDirection.INCREASING, false);
+ MathUtils.checkOrder(new double[] {3, -5.5, -11, -27.5},
+ MathUtils.OrderDirection.DECREASING, true);
+ MathUtils.checkOrder(new double[] {3, 0, 0, -5.5, -11, -27.5},
+ MathUtils.OrderDirection.DECREASING, false);
+
+ try {
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, -1, 2, 15},
+ MathUtils.OrderDirection.INCREASING, true);
+ fail("an exception should have been thrown");
+ } catch (NonMonotonousSequenceException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {-15, -5.5, -1, -2, 2},
+ MathUtils.OrderDirection.INCREASING, false);
+ fail("an exception should have been thrown");
+ } catch (NonMonotonousSequenceException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {3, 3, -5.5, -11, -27.5},
+ MathUtils.OrderDirection.DECREASING, true);
+ fail("an exception should have been thrown");
+ } catch (NonMonotonousSequenceException e) {
+ // Expected
+ }
+ try {
+ MathUtils.checkOrder(new double[] {3, -1, 0, -5.5, -11, -27.5},
+ MathUtils.OrderDirection.DECREASING, false);
+ fail("an exception should have been thrown");
+ } catch (NonMonotonousSequenceException e) {
+ // Expected
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/MultidimensionalCounterTest.java b/src/test/java/org/apache/commons/math/util/MultidimensionalCounterTest.java
new file mode 100644
index 0000000..827030d
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/MultidimensionalCounterTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.util;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.OutOfRangeException;
+import org.apache.commons.math.exception.NotStrictlyPositiveException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class MultidimensionalCounterTest {
+ @Test
+ public void testPreconditions() {
+ MultidimensionalCounter c;
+
+ try {
+ c = new MultidimensionalCounter(0, 1);
+ Assert.fail("NotStrictlyPositiveException expected");
+ } catch (NotStrictlyPositiveException e) {
+ // Expected.
+ }
+ try {
+ c = new MultidimensionalCounter(2, 0);
+ Assert.fail("NotStrictlyPositiveException expected");
+ } catch (NotStrictlyPositiveException e) {
+ // Expected.
+ }
+ try {
+ c = new MultidimensionalCounter(-1, 1);
+ Assert.fail("NotStrictlyPositiveException expected");
+ } catch (NotStrictlyPositiveException e) {
+ // Expected.
+ }
+
+ c = new MultidimensionalCounter(2, 3);
+ try {
+ c.getCount(1, 1, 1);
+ Assert.fail("DimensionMismatchException expected");
+ } catch (DimensionMismatchException e) {
+ // Expected.
+ }
+ try {
+ c.getCount(3, 1);
+ Assert.fail("OutOfRangeException expected");
+ } catch (OutOfRangeException e) {
+ // Expected.
+ }
+ try {
+ c.getCount(0, -1);
+ Assert.fail("OutOfRangeException expected");
+ } catch (OutOfRangeException e) {
+ // Expected.
+ }
+ try {
+ c.getCounts(-1);
+ Assert.fail("OutOfRangeException expected");
+ } catch (OutOfRangeException e) {
+ // Expected.
+ }
+ try {
+ c.getCounts(6);
+ Assert.fail("OutOfRangeException expected");
+ } catch (OutOfRangeException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testIteratorPreconditions() {
+ MultidimensionalCounter.Iterator iter = (new MultidimensionalCounter(2, 3)).iterator();
+ try {
+ iter.getCount(-1);
+ Assert.fail("IndexOutOfBoundsException expected");
+ } catch (IndexOutOfBoundsException e) {
+ // Expected.
+ }
+ try {
+ iter.getCount(2);
+ Assert.fail("IndexOutOfBoundsException expected");
+ } catch (IndexOutOfBoundsException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testMulti2UniConversion() {
+ final MultidimensionalCounter c = new MultidimensionalCounter(2, 4, 5);
+ Assert.assertEquals(c.getCount(1, 2, 3), 33);
+ }
+
+ @Test
+ public void testAccessors() {
+ final int[] originalSize = new int[] {2, 6, 5};
+ final MultidimensionalCounter c = new MultidimensionalCounter(originalSize);
+ final int nDim = c.getDimension();
+ Assert.assertEquals(nDim, originalSize.length);
+
+ final int[] size = c.getSizes();
+ for (int i = 0; i < nDim; i++) {
+ Assert.assertEquals(originalSize[i], size[i]);
+ }
+ }
+
+ @Test
+ public void testIterationConsistency() {
+ final MultidimensionalCounter c = new MultidimensionalCounter(2, 3, 2);
+ final int[][] expected = new int[][] {
+ { 0, 0, 0 },
+ { 0, 0, 1 },
+ { 0, 1, 0 },
+ { 0, 1, 1 },
+ { 0, 2, 0 },
+ { 0, 2, 1 },
+ { 1, 0, 0 },
+ { 1, 0, 1 },
+ { 1, 1, 0 },
+ { 1, 1, 1 },
+ { 1, 2, 0 },
+ { 1, 2, 1 }
+ };
+
+ final int totalSize = c.getSize();
+ final int nDim = c.getDimension();
+ final MultidimensionalCounter.Iterator iter = c.iterator();
+ for (int i = 0; i < totalSize; i++) {
+ if (!iter.hasNext()) {
+ Assert.fail("Too short");
+ }
+ final int uniDimIndex = iter.next();
+ Assert.assertEquals("Wrong iteration at " + i, i, uniDimIndex);
+
+ for (int dimIndex = 0; dimIndex < nDim; dimIndex++) {
+ Assert.assertEquals("Wrong multidimensional index for [" + i + "][" + dimIndex + "]",
+ expected[i][dimIndex], iter.getCount(dimIndex));
+ }
+
+ Assert.assertEquals("Wrong unidimensional index for [" + i + "]",
+ c.getCount(expected[i]), uniDimIndex);
+
+ final int[] indices = c.getCounts(uniDimIndex);
+ for (int dimIndex = 0; dimIndex < nDim; dimIndex++) {
+ Assert.assertEquals("Wrong multidimensional index for [" + i + "][" + dimIndex + "]",
+ expected[i][dimIndex], indices[dimIndex]);
+ }
+ }
+
+ if (iter.hasNext()) {
+ Assert.fail("Too long");
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/math/util/OpenIntToDoubleHashMapTest.java b/src/test/java/org/apache/commons/math/util/OpenIntToDoubleHashMapTest.java
new file mode 100644
index 0000000..5c0c641
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/OpenIntToDoubleHashMapTest.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.util;
+
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the {@link OpenIntToDoubleHashMap}.
+ */
+public class OpenIntToDoubleHashMapTest extends TestCase {
+
+ private Map<Integer, Double> javaMap = new HashMap<Integer, Double>();
+
+ @Override
+ protected void setUp() throws Exception {
+ javaMap.put(50, 100.0);
+ javaMap.put(75, 75.0);
+ javaMap.put(25, 500.0);
+ javaMap.put(Integer.MAX_VALUE, Double.MAX_VALUE);
+ javaMap.put(0, -1.0);
+ javaMap.put(1, 0.0);
+ javaMap.put(33, -0.1);
+ javaMap.put(23234234, -242343.0);
+ javaMap.put(23321, Double.MIN_VALUE);
+ javaMap.put(-4444, 332.0);
+ javaMap.put(-1, -2323.0);
+ javaMap.put(Integer.MIN_VALUE, 44.0);
+
+ /* Add a few more to cause the table to rehash */
+ javaMap.putAll(generate());
+
+ }
+
+ private Map<Integer, Double> generate() {
+ Map<Integer, Double> map = new HashMap<Integer, Double>();
+ Random r = new Random();
+ for (int i = 0; i < 2000; ++i)
+ map.put(r.nextInt(), r.nextDouble());
+ return map;
+ }
+
+ private OpenIntToDoubleHashMap createFromJavaMap() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ }
+ return map;
+ }
+
+ public void testPutAndGetWith0ExpectedSize() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap(0);
+ assertPutAndGet(map);
+ }
+
+ public void testPutAndGetWithExpectedSize() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap(500);
+ assertPutAndGet(map);
+ }
+
+ public void testPutAndGet() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ assertPutAndGet(map);
+ }
+
+ private void assertPutAndGet(OpenIntToDoubleHashMap map) {
+ assertPutAndGet(map, 0, new HashSet<Integer>());
+ }
+
+ private void assertPutAndGet(OpenIntToDoubleHashMap map, int mapSize,
+ Set<Integer> keysInMap) {
+ assertEquals(mapSize, map.size());
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ if (!keysInMap.contains(mapEntry.getKey()))
+ ++mapSize;
+ assertEquals(mapSize, map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testPutAbsentOnExisting() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ int size = javaMap.size();
+ for (Map.Entry<Integer, Double> mapEntry : generateAbsent().entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ assertEquals(++size, map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testPutOnExisting() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ assertEquals(javaMap.size(), map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testGetAbsent() {
+ Map<Integer, Double> generated = generateAbsent();
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+
+ for (Map.Entry<Integer, Double> mapEntry : generated.entrySet())
+ assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
+ }
+
+ public void testGetFromEmpty() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ assertTrue(Double.isNaN(map.get(5)));
+ assertTrue(Double.isNaN(map.get(0)));
+ assertTrue(Double.isNaN(map.get(50)));
+ }
+
+ public void testRemove() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ int mapSize = javaMap.size();
+ assertEquals(mapSize, map.size());
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ map.remove(mapEntry.getKey());
+ assertEquals(--mapSize, map.size());
+ assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
+ }
+
+ /* Ensure that put and get still work correctly after removals */
+ assertPutAndGet(map);
+ }
+
+ /* This time only remove some entries */
+ public void testRemove2() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ int mapSize = javaMap.size();
+ int count = 0;
+ Set<Integer> keysInMap = new HashSet<Integer>(javaMap.keySet());
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ keysInMap.remove(mapEntry.getKey());
+ map.remove(mapEntry.getKey());
+ assertEquals(--mapSize, map.size());
+ assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
+ if (count++ > 5)
+ break;
+ }
+
+ /* Ensure that put and get still work correctly after removals */
+ assertPutAndGet(map, mapSize, keysInMap);
+ }
+
+ public void testRemoveFromEmpty() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ assertTrue(Double.isNaN(map.remove(50)));
+ }
+
+ public void testRemoveAbsent() {
+ Map<Integer, Double> generated = generateAbsent();
+
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ int mapSize = map.size();
+
+ for (Map.Entry<Integer, Double> mapEntry : generated.entrySet()) {
+ map.remove(mapEntry.getKey());
+ assertEquals(mapSize, map.size());
+ assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
+ }
+ }
+
+ /**
+ * Returns a map with at least 100 elements where each element is absent from javaMap.
+ */
+ private Map<Integer, Double> generateAbsent() {
+ Map<Integer, Double> generated = new HashMap<Integer, Double>();
+ do {
+ generated.putAll(generate());
+ for (Integer key : javaMap.keySet())
+ generated.remove(key);
+ } while (generated.size() < 100);
+ return generated;
+ }
+
+ public void testCopy() {
+ OpenIntToDoubleHashMap copy =
+ new OpenIntToDoubleHashMap(createFromJavaMap());
+ assertEquals(javaMap.size(), copy.size());
+
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet())
+ assertEquals(mapEntry.getValue(), copy.get(mapEntry.getKey()));
+ }
+
+ public void testContainsKey() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ assertTrue(map.containsKey(mapEntry.getKey()));
+ }
+ for (Map.Entry<Integer, Double> mapEntry : generateAbsent().entrySet()) {
+ assertFalse(map.containsKey(mapEntry.getKey()));
+ }
+ for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
+ int key = mapEntry.getKey();
+ assertTrue(map.containsKey(key));
+ map.remove(key);
+ assertFalse(map.containsKey(key));
+ }
+ }
+
+ public void testIterator() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ OpenIntToDoubleHashMap.Iterator iterator = map.iterator();
+ for (int i = 0; i < map.size(); ++i) {
+ assertTrue(iterator.hasNext());
+ iterator.advance();
+ int key = iterator.key();
+ assertTrue(map.containsKey(key));
+ assertEquals(javaMap.get(key), map.get(key), 0);
+ assertEquals(javaMap.get(key), iterator.value(), 0);
+ assertTrue(javaMap.containsKey(key));
+ }
+ assertFalse(iterator.hasNext());
+ try {
+ iterator.advance();
+ fail("an exception should have been thrown");
+ } catch (NoSuchElementException nsee) {
+ // expected
+ }
+ }
+
+ public void testConcurrentModification() {
+ OpenIntToDoubleHashMap map = createFromJavaMap();
+ OpenIntToDoubleHashMap.Iterator iterator = map.iterator();
+ map.put(3, 3);
+ try {
+ iterator.advance();
+ fail("an exception should have been thrown");
+ } catch (ConcurrentModificationException cme) {
+ // expected
+ }
+ }
+
+ /**
+ * Regression test for a bug in findInsertionIndex where the hashing in the second probing
+ * loop was inconsistent with the first causing duplicate keys after the right sequence
+ * of puts and removes.
+ */
+ public void testPutKeysWithCollisions() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ int key1 = -1996012590;
+ double value1 = 1.0;
+ map.put(key1, value1);
+ int key2 = 835099822;
+ map.put(key2, value1);
+ int key3 = 1008859686;
+ map.put(key3, value1);
+ assertEquals(value1, map.get(key3));
+ assertEquals(3, map.size());
+
+ map.remove(key2);
+ double value2 = 2.0;
+ map.put(key3, value2);
+ assertEquals(value2, map.get(key3));
+ assertEquals(2, map.size());
+ }
+
+ /**
+ * Similar to testPutKeysWithCollisions() but exercises the codepaths in a slightly
+ * different manner.
+ */
+ public void testPutKeysWithCollision2() {
+ OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
+ int key1 = 837989881;
+ double value1 = 1.0;
+ map.put(key1, value1);
+ int key2 = 476463321;
+ map.put(key2, value1);
+ assertEquals(2, map.size());
+ assertEquals(value1, map.get(key2));
+
+ map.remove(key1);
+ double value2 = 2.0;
+ map.put(key2, value2);
+ assertEquals(1, map.size());
+ assertEquals(value2, map.get(key2));
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/OpenIntToFieldTest.java b/src/test/java/org/apache/commons/math/util/OpenIntToFieldTest.java
new file mode 100644
index 0000000..f47fdce
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/OpenIntToFieldTest.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.apache.commons.math.Field;
+import org.apache.commons.math.fraction.Fraction;
+import org.apache.commons.math.fraction.FractionConversionException;
+import org.apache.commons.math.fraction.FractionField;
+
+import junit.framework.TestCase;
+
+public class OpenIntToFieldTest extends TestCase {
+
+ private Map<Integer, Fraction> javaMap = new HashMap<Integer, Fraction>();
+ private FractionField field = FractionField.getInstance();
+
+ @Override
+ protected void setUp() throws Exception {
+ javaMap.put(50, new Fraction(100.0));
+ javaMap.put(75, new Fraction(75.0));
+ javaMap.put(25, new Fraction(500.0));
+ javaMap.put(Integer.MAX_VALUE, new Fraction(Integer.MAX_VALUE));
+ javaMap.put(0, new Fraction(-1.0));
+ javaMap.put(1, new Fraction(0.0));
+ javaMap.put(33, new Fraction(-0.1));
+ javaMap.put(23234234, new Fraction(-242343.0));
+ javaMap.put(23321, new Fraction (Integer.MIN_VALUE));
+ javaMap.put(-4444, new Fraction(332.0));
+ javaMap.put(-1, new Fraction(-2323.0));
+ javaMap.put(Integer.MIN_VALUE, new Fraction(44.0));
+
+ /* Add a few more to cause the table to rehash */
+ javaMap.putAll(generate());
+
+ }
+
+ private Map<Integer, Fraction> generate() {
+ Map<Integer, Fraction> map = new HashMap<Integer, Fraction>();
+ Random r = new Random();
+ double dd=0;
+ for (int i = 0; i < 2000; ++i)
+ dd = r.nextDouble();
+ try {
+ map.put(r.nextInt(), new Fraction(dd));
+ } catch (FractionConversionException e) {
+ throw new IllegalStateException("Invalid :"+dd, e);
+ }
+ return map;
+ }
+
+ private OpenIntToFieldHashMap<Fraction> createFromJavaMap(Field<Fraction> field) {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field);
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ }
+ return map;
+ }
+
+ public void testPutAndGetWith0ExpectedSize() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field,0);
+ assertPutAndGet(map);
+ }
+
+ public void testPutAndGetWithExpectedSize() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field,500);
+ assertPutAndGet(map);
+ }
+
+ public void testPutAndGet() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field);
+ assertPutAndGet(map);
+ }
+
+ private void assertPutAndGet(OpenIntToFieldHashMap<Fraction> map) {
+ assertPutAndGet(map, 0, new HashSet<Integer>());
+ }
+
+ private void assertPutAndGet(OpenIntToFieldHashMap<Fraction> map, int mapSize,
+ Set<Integer> keysInMap) {
+ assertEquals(mapSize, map.size());
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ if (!keysInMap.contains(mapEntry.getKey()))
+ ++mapSize;
+ assertEquals(mapSize, map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testPutAbsentOnExisting() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ int size = javaMap.size();
+ for (Map.Entry<Integer, Fraction> mapEntry : generateAbsent().entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ assertEquals(++size, map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testPutOnExisting() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ map.put(mapEntry.getKey(), mapEntry.getValue());
+ assertEquals(javaMap.size(), map.size());
+ assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
+ }
+ }
+
+ public void testGetAbsent() {
+ Map<Integer, Fraction> generated = generateAbsent();
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+
+ for (Map.Entry<Integer, Fraction> mapEntry : generated.entrySet())
+ assertTrue(field.getZero().equals(map.get(mapEntry.getKey())));
+ }
+
+ public void testGetFromEmpty() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field);
+ assertTrue(field.getZero().equals(map.get(5)));
+ assertTrue(field.getZero().equals(map.get(0)));
+ assertTrue(field.getZero().equals(map.get(50)));
+ }
+
+ public void testRemove() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ int mapSize = javaMap.size();
+ assertEquals(mapSize, map.size());
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ map.remove(mapEntry.getKey());
+ assertEquals(--mapSize, map.size());
+ assertTrue(field.getZero().equals(map.get(mapEntry.getKey())));
+ }
+
+ /* Ensure that put and get still work correctly after removals */
+ assertPutAndGet(map);
+ }
+
+ /* This time only remove some entries */
+ public void testRemove2() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ int mapSize = javaMap.size();
+ int count = 0;
+ Set<Integer> keysInMap = new HashSet<Integer>(javaMap.keySet());
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ keysInMap.remove(mapEntry.getKey());
+ map.remove(mapEntry.getKey());
+ assertEquals(--mapSize, map.size());
+ assertTrue(field.getZero().equals(map.get(mapEntry.getKey())));
+ if (count++ > 5)
+ break;
+ }
+
+ /* Ensure that put and get still work correctly after removals */
+ assertPutAndGet(map, mapSize, keysInMap);
+ }
+
+ public void testRemoveFromEmpty() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field);
+ assertTrue(field.getZero().equals(map.remove(50)));
+ }
+
+ public void testRemoveAbsent() {
+ Map<Integer, Fraction> generated = generateAbsent();
+
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ int mapSize = map.size();
+
+ for (Map.Entry<Integer, Fraction> mapEntry : generated.entrySet()) {
+ map.remove(mapEntry.getKey());
+ assertEquals(mapSize, map.size());
+ assertTrue(field.getZero().equals(map.get(mapEntry.getKey())));
+ }
+ }
+
+ /**
+ * Returns a map with at least 100 elements where each element is absent from javaMap.
+ */
+ private Map<Integer, Fraction> generateAbsent() {
+ Map<Integer, Fraction> generated = new HashMap<Integer, Fraction>();
+ do {
+ generated.putAll(generate());
+ for (Integer key : javaMap.keySet())
+ generated.remove(key);
+ } while (generated.size() < 100);
+ return generated;
+ }
+
+ public void testCopy() {
+ OpenIntToFieldHashMap<Fraction> copy =
+ new OpenIntToFieldHashMap<Fraction>(createFromJavaMap(field));
+ assertEquals(javaMap.size(), copy.size());
+
+ for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet())
+ assertEquals(mapEntry.getValue(), copy.get(mapEntry.getKey()));
+ }
+
+ public void testContainsKey() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ for (Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ assertTrue(map.containsKey(mapEntry.getKey()));
+ }
+ for (Map.Entry<Integer, Fraction> mapEntry : generateAbsent().entrySet()) {
+ assertFalse(map.containsKey(mapEntry.getKey()));
+ }
+ for (Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
+ int key = mapEntry.getKey();
+ assertTrue(map.containsKey(key));
+ map.remove(key);
+ assertFalse(map.containsKey(key));
+ }
+ }
+
+ public void testIterator() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ OpenIntToFieldHashMap<Fraction>.Iterator iterator = map.iterator();
+ for (int i = 0; i < map.size(); ++i) {
+ assertTrue(iterator.hasNext());
+ iterator.advance();
+ int key = iterator.key();
+ assertTrue(map.containsKey(key));
+ assertEquals(javaMap.get(key), map.get(key));
+ assertEquals(javaMap.get(key), iterator.value());
+ assertTrue(javaMap.containsKey(key));
+ }
+ assertFalse(iterator.hasNext());
+ try {
+ iterator.advance();
+ fail("an exception should have been thrown");
+ } catch (NoSuchElementException nsee) {
+ // expected
+ }
+ }
+
+ public void testConcurrentModification() {
+ OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
+ OpenIntToFieldHashMap<Fraction>.Iterator iterator = map.iterator();
+ map.put(3, new Fraction(3));
+ try {
+ iterator.advance();
+ fail("an exception should have been thrown");
+ } catch (ConcurrentModificationException cme) {
+ // expected
+ }
+ }
+
+ /**
+ * Regression test for a bug in findInsertionIndex where the hashing in the second probing
+ * loop was inconsistent with the first causing duplicate keys after the right sequence
+ * of puts and removes.
+ */
+ public void testPutKeysWithCollisions() {
+ OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<Fraction>(field);
+ int key1 = -1996012590;
+ Fraction value1 = new Fraction(1);
+ map.put(key1, value1);
+ int key2 = 835099822;
+ map.put(key2, value1);
+ int key3 = 1008859686;
+ map.put(key3, value1);
+ assertEquals(value1, map.get(key3));
+ assertEquals(3, map.size());
+
+ map.remove(key2);
+ Fraction value2 = new Fraction(2);
+ map.put(key3, value2);
+ assertEquals(value2, map.get(key3));
+ assertEquals(2, map.size());
+ }
+
+ /**
+ * Similar to testPutKeysWithCollisions() but exercises the codepaths in a slightly
+ * different manner.
+ */
+ public void testPutKeysWithCollision2() {
+ OpenIntToFieldHashMap<Fraction>map = new OpenIntToFieldHashMap<Fraction>(field);
+ int key1 = 837989881;
+ Fraction value1 = new Fraction(1);
+ map.put(key1, value1);
+ int key2 = 476463321;
+ map.put(key2, value1);
+ assertEquals(2, map.size());
+ assertEquals(value1, map.get(key2));
+
+ map.remove(key1);
+ Fraction value2 = new Fraction(2);
+ map.put(key2, value2);
+ assertEquals(1, map.size());
+ assertEquals(value2, map.get(key2));
+ }
+
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/ResizableDoubleArrayTest.java b/src/test/java/org/apache/commons/math/util/ResizableDoubleArrayTest.java
new file mode 100644
index 0000000..ac539b8
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/ResizableDoubleArrayTest.java
@@ -0,0 +1,540 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math.util;
+import org.apache.commons.math.random.RandomDataImpl;
+import org.apache.commons.math.random.RandomData;
+
+
+/**
+ * This class contains test cases for the ResizableDoubleArray.
+ *
+ * @version $Revision: 1053282 $ $Date: 2010-12-28 10:03:40 +0100 (mar. 28 déc. 2010) $
+ */
+public class ResizableDoubleArrayTest extends DoubleArrayAbstractTest {
+
+ public ResizableDoubleArrayTest(String name) {
+ super( name );
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ da = null;
+ ra = null;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ da = new ResizableDoubleArray();
+ ra = new ResizableDoubleArray();
+ }
+
+ public void testConstructors() {
+ float defaultExpansionFactor = 2.0f;
+ float defaultContractionCriteria = 2.5f;
+ int defaultMode = ResizableDoubleArray.MULTIPLICATIVE_MODE;
+
+ ResizableDoubleArray testDa = new ResizableDoubleArray(2);
+ assertEquals(0, testDa.getNumElements());
+ assertEquals(2, testDa.getInternalLength());
+ assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
+ assertEquals(defaultContractionCriteria, testDa.getContractionCriteria(), 0);
+ assertEquals(defaultMode, testDa.getExpansionMode());
+ try {
+ da = new ResizableDoubleArray(-1);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ testDa = new ResizableDoubleArray((double[]) null);
+ assertEquals(0, testDa.getNumElements());
+
+ double[] initialArray = new double[] { 0, 1, 2 };
+ testDa = new ResizableDoubleArray(initialArray);
+ assertEquals(3, testDa.getNumElements());
+
+ testDa = new ResizableDoubleArray(2, 2.0f);
+ assertEquals(0, testDa.getNumElements());
+ assertEquals(2, testDa.getInternalLength());
+ assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
+ assertEquals(defaultContractionCriteria, testDa.getContractionCriteria(), 0);
+ assertEquals(defaultMode, testDa.getExpansionMode());
+
+ try {
+ da = new ResizableDoubleArray(2, 0.5f);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ testDa = new ResizableDoubleArray(2, 3.0f);
+ assertEquals(3.0f, testDa.getExpansionFactor(), 0);
+ assertEquals(3.5f, testDa.getContractionCriteria(), 0);
+
+ testDa = new ResizableDoubleArray(2, 2.0f, 3.0f);
+ assertEquals(0, testDa.getNumElements());
+ assertEquals(2, testDa.getInternalLength());
+ assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
+ assertEquals(3.0f, testDa.getContractionCriteria(), 0);
+ assertEquals(defaultMode, testDa.getExpansionMode());
+
+ try {
+ da = new ResizableDoubleArray(2, 2.0f, 1.5f);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ testDa = new ResizableDoubleArray(2, 2.0f, 3.0f,
+ ResizableDoubleArray.ADDITIVE_MODE);
+ assertEquals(0, testDa.getNumElements());
+ assertEquals(2, testDa.getInternalLength());
+ assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
+ assertEquals(3.0f, testDa.getContractionCriteria(), 0);
+ assertEquals(ResizableDoubleArray.ADDITIVE_MODE,
+ testDa.getExpansionMode());
+
+ try {
+ da = new ResizableDoubleArray(2, 2.0f, 2.5f, -1);
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+
+ // Copy constructor
+ testDa = new ResizableDoubleArray(2, 2.0f, 3.0f,
+ ResizableDoubleArray.ADDITIVE_MODE);
+ testDa.addElement(2.0);
+ testDa.addElement(3.2);
+ ResizableDoubleArray copyDa = new ResizableDoubleArray(testDa);
+ assertEquals(copyDa, testDa);
+ assertEquals(testDa, copyDa);
+ }
+
+
+ public void testSetElementArbitraryExpansion() {
+
+ // MULTIPLICATIVE_MODE
+ da.addElement(2.0);
+ da.addElement(4.0);
+ da.addElement(6.0);
+ da.setElement(1, 3.0);
+
+ // Expand the array arbitrarily to 1000 items
+ da.setElement(1000, 3.4);
+
+ assertEquals( "The number of elements should now be 1001, it isn't",
+ da.getNumElements(), 1001);
+
+ assertEquals( "Uninitialized Elements are default value of 0.0, index 766 wasn't", 0.0,
+ da.getElement( 760 ), Double.MIN_VALUE );
+
+ assertEquals( "The 1000th index should be 3.4, it isn't", 3.4, da.getElement(1000),
+ Double.MIN_VALUE );
+ assertEquals( "The 0th index should be 2.0, it isn't", 2.0, da.getElement(0),
+ Double.MIN_VALUE);
+
+ // Make sure numElements and expansion work correctly for expansion boundary cases
+ da.clear();
+ da.addElement(2.0);
+ da.addElement(4.0);
+ da.addElement(6.0);
+ assertEquals(4, ((ResizableDoubleArray) da).getInternalLength());
+ assertEquals(3, da.getNumElements());
+ da.setElement(3, 7.0);
+ assertEquals(4, ((ResizableDoubleArray) da).getInternalLength());
+ assertEquals(4, da.getNumElements());
+ da.setElement(10, 10.0);
+ assertEquals(11, ((ResizableDoubleArray) da).getInternalLength());
+ assertEquals(11, da.getNumElements());
+ da.setElement(9, 10.0);
+ assertEquals(11, ((ResizableDoubleArray) da).getInternalLength());
+ assertEquals(11, da.getNumElements());
+
+ try {
+ da.setElement(-2, 3);
+ fail("Expecting ArrayIndexOutOfBoundsException for negative index");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // expected
+ }
+
+ // ADDITIVE_MODE
+
+ ResizableDoubleArray testDa = new ResizableDoubleArray(2, 2.0f, 3.0f,
+ ResizableDoubleArray.ADDITIVE_MODE);
+ assertEquals(2, testDa.getInternalLength());
+ testDa.addElement(1d);
+ testDa.addElement(1d);
+ assertEquals(2, testDa.getInternalLength());
+ testDa.addElement(1d);
+ assertEquals(4, testDa.getInternalLength());
+ }
+
+ @Override
+ public void testAdd1000() {
+ super.testAdd1000();
+ assertEquals("Internal Storage length should be 1024 if we started out with initial capacity of " +
+ "16 and an expansion factor of 2.0",
+ 1024, ((ResizableDoubleArray) da).getInternalLength());
+ }
+
+ public void testAddElements() {
+ ResizableDoubleArray testDa = new ResizableDoubleArray();
+
+ // MULTIPLICATIVE_MODE
+ testDa.addElements(new double[] {4, 5, 6});
+ assertEquals(3, testDa.getNumElements(), 0);
+ assertEquals(4, testDa.getElement(0), 0);
+ assertEquals(5, testDa.getElement(1), 0);
+ assertEquals(6, testDa.getElement(2), 0);
+
+ testDa.addElements(new double[] {4, 5, 6});
+ assertEquals(6, testDa.getNumElements());
+
+ // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
+ testDa = new ResizableDoubleArray(2, 2.0f, 2.5f,
+ ResizableDoubleArray.ADDITIVE_MODE);
+ assertEquals(2, testDa.getInternalLength());
+ testDa.addElements(new double[] { 1d }); // x,0
+ testDa.addElements(new double[] { 2d }); // x,x
+ testDa.addElements(new double[] { 3d }); // x,x,x,0 -- expanded
+ assertEquals(1d, testDa.getElement(0), 0);
+ assertEquals(2d, testDa.getElement(1), 0);
+ assertEquals(3d, testDa.getElement(2), 0);
+ assertEquals(4, testDa.getInternalLength()); // x,x,x,0
+ assertEquals(3, testDa.getNumElements());
+ }
+
+ @Override
+ public void testAddElementRolling() {
+ super.testAddElementRolling();
+
+ // MULTIPLICATIVE_MODE
+ da.clear();
+ da.addElement(1);
+ da.addElement(2);
+ da.addElementRolling(3);
+ assertEquals(3, da.getElement(1), 0);
+ da.addElementRolling(4);
+ assertEquals(3, da.getElement(0), 0);
+ assertEquals(4, da.getElement(1), 0);
+ da.addElement(5);
+ assertEquals(5, da.getElement(2), 0);
+ da.addElementRolling(6);
+ assertEquals(4, da.getElement(0), 0);
+ assertEquals(5, da.getElement(1), 0);
+ assertEquals(6, da.getElement(2), 0);
+
+ // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
+ ResizableDoubleArray testDa = new ResizableDoubleArray(2, 2.0f, 2.5f,
+ ResizableDoubleArray.ADDITIVE_MODE);
+ assertEquals(2, testDa.getInternalLength());
+ testDa.addElement(1d); // x,0
+ testDa.addElement(2d); // x,x
+ testDa.addElement(3d); // x,x,x,0 -- expanded
+ assertEquals(1d, testDa.getElement(0), 0);
+ assertEquals(2d, testDa.getElement(1), 0);
+ assertEquals(3d, testDa.getElement(2), 0);
+ assertEquals(4, testDa.getInternalLength()); // x,x,x,0
+ assertEquals(3, testDa.getNumElements());
+ testDa.addElementRolling(4d);
+ assertEquals(2d, testDa.getElement(0), 0);
+ assertEquals(3d, testDa.getElement(1), 0);
+ assertEquals(4d, testDa.getElement(2), 0);
+ assertEquals(4, testDa.getInternalLength()); // 0,x,x,x
+ assertEquals(3, testDa.getNumElements());
+ testDa.addElementRolling(5d); // 0,0,x,x,x,0 -- time to contract
+ assertEquals(3d, testDa.getElement(0), 0);
+ assertEquals(4d, testDa.getElement(1), 0);
+ assertEquals(5d, testDa.getElement(2), 0);
+ assertEquals(4, testDa.getInternalLength()); // contracted -- x,x,x,0
+ assertEquals(3, testDa.getNumElements());
+ try {
+ testDa.getElement(4);
+ fail("Expecting ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // expected
+ }
+ try {
+ testDa.getElement(-1);
+ fail("Expecting ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // expected
+ }
+ }
+
+ public void testSetNumberOfElements() {
+ da.addElement( 1.0 );
+ da.addElement( 1.0 );
+ da.addElement( 1.0 );
+ da.addElement( 1.0 );
+ da.addElement( 1.0 );
+ da.addElement( 1.0 );
+ assertEquals( "Number of elements should equal 6", da.getNumElements(), 6);
+
+ ((ResizableDoubleArray) da).setNumElements( 3 );
+ assertEquals( "Number of elements should equal 3", da.getNumElements(), 3);
+
+ try {
+ ((ResizableDoubleArray) da).setNumElements( -3 );
+ fail( "Setting number of elements to negative should've thrown an exception");
+ } catch( IllegalArgumentException iae ) {
+ }
+
+ ((ResizableDoubleArray) da).setNumElements(1024);
+ assertEquals( "Number of elements should now be 1024", da.getNumElements(), 1024);
+ assertEquals( "Element 453 should be a default double", da.getElement( 453 ), 0.0, Double.MIN_VALUE);
+
+ }
+
+ public void testWithInitialCapacity() {
+
+ ResizableDoubleArray eDA2 = new ResizableDoubleArray(2);
+ assertEquals("Initial number of elements should be 0", 0, eDA2.getNumElements());
+
+ RandomData randomData = new RandomDataImpl();
+ int iterations = randomData.nextInt(100, 1000);
+
+ for( int i = 0; i < iterations; i++) {
+ eDA2.addElement( i );
+ }
+
+ assertEquals("Number of elements should be equal to " + iterations, iterations, eDA2.getNumElements());
+
+ eDA2.addElement( 2.0 );
+
+ assertEquals("Number of elements should be equals to " + (iterations +1),
+ iterations + 1 , eDA2.getNumElements() );
+ }
+
+ public void testWithInitialCapacityAndExpansionFactor() {
+
+ ResizableDoubleArray eDA3 = new ResizableDoubleArray(3, 3.0f, 3.5f);
+ assertEquals("Initial number of elements should be 0", 0, eDA3.getNumElements() );
+
+ RandomData randomData = new RandomDataImpl();
+ int iterations = randomData.nextInt(100, 3000);
+
+ for( int i = 0; i < iterations; i++) {
+ eDA3.addElement( i );
+ }
+
+ assertEquals("Number of elements should be equal to " + iterations, iterations,eDA3.getNumElements());
+
+ eDA3.addElement( 2.0 );
+
+ assertEquals("Number of elements should be equals to " + (iterations +1),
+ iterations +1, eDA3.getNumElements() );
+
+ assertEquals("Expansion factor should equal 3.0", 3.0f, eDA3.getExpansionFactor(), Double.MIN_VALUE);
+ }
+
+ public void testDiscard() {
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ assertEquals( "Number of elements should be 11", 11, da.getNumElements());
+
+ ((ResizableDoubleArray)da).discardFrontElements(5);
+ assertEquals( "Number of elements should be 6", 6, da.getNumElements());
+
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ assertEquals( "Number of elements should be 10", 10, da.getNumElements());
+
+ ((ResizableDoubleArray)da).discardMostRecentElements(2);
+ assertEquals( "Number of elements should be 8", 8, da.getNumElements());
+
+ try {
+ ((ResizableDoubleArray)da).discardFrontElements(-1);
+ fail( "Trying to discard a negative number of element is not allowed");
+ } catch( Exception e ){
+ }
+
+ try {
+ ((ResizableDoubleArray)da).discardMostRecentElements(-1);
+ fail( "Trying to discard a negative number of element is not allowed");
+ } catch( Exception e ){
+ }
+
+ try {
+ ((ResizableDoubleArray)da).discardFrontElements( 10000 );
+ fail( "You can't discard more elements than the array contains");
+ } catch( Exception e ){
+ }
+
+ try {
+ ((ResizableDoubleArray)da).discardMostRecentElements( 10000 );
+ fail( "You can't discard more elements than the array contains");
+ } catch( Exception e ){
+ }
+
+ }
+
+ public void testSubstitute() {
+
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ da.addElement(2.0);
+ assertEquals( "Number of elements should be 11", 11, da.getNumElements());
+
+ ((ResizableDoubleArray)da).substituteMostRecentElement(24);
+
+ assertEquals( "Number of elements should be 11", 11, da.getNumElements());
+
+ try {
+ ((ResizableDoubleArray)da).discardMostRecentElements(10);
+ } catch( Exception e ){
+ fail( "Trying to discard a negative number of element is not allowed");
+ }
+
+ ((ResizableDoubleArray)da).substituteMostRecentElement(24);
+
+ assertEquals( "Number of elements should be 1", 1, da.getNumElements());
+
+ }
+
+ public void testMutators() {
+ ((ResizableDoubleArray)da).setContractionCriteria(10f);
+ assertEquals(10f, ((ResizableDoubleArray)da).getContractionCriteria(), 0);
+ ((ResizableDoubleArray)da).setExpansionFactor(8f);
+ assertEquals(8f, ((ResizableDoubleArray)da).getExpansionFactor(), 0);
+ try {
+ ((ResizableDoubleArray)da).setExpansionFactor(11f); // greater than contractionCriteria
+ fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ ((ResizableDoubleArray)da).setExpansionMode(
+ ResizableDoubleArray.ADDITIVE_MODE);
+ assertEquals(ResizableDoubleArray.ADDITIVE_MODE,
+ ((ResizableDoubleArray)da).getExpansionMode());
+ try {
+ ((ResizableDoubleArray)da).setExpansionMode(-1);
+ fail ("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+
+ // Wrong type
+ ResizableDoubleArray first = new ResizableDoubleArray();
+ Double other = new Double(2);
+ assertFalse(first.equals(other));
+
+ // Null
+ other = null;
+ assertFalse(first.equals(other));
+
+ // Reflexive
+ assertTrue(first.equals(first));
+
+ // Argumentless constructor
+ ResizableDoubleArray second = new ResizableDoubleArray();
+ verifyEquality(first, second);
+
+ // Equals iff same data, same properties
+ ResizableDoubleArray third = new ResizableDoubleArray(3, 2.0f, 2.0f);
+ verifyInequality(third, first);
+ ResizableDoubleArray fourth = new ResizableDoubleArray(3, 2.0f, 2.0f);
+ ResizableDoubleArray fifth = new ResizableDoubleArray(2, 2.0f, 2.0f);
+ verifyEquality(third, fourth);
+ verifyInequality(third, fifth);
+ third.addElement(4.1);
+ third.addElement(4.2);
+ third.addElement(4.3);
+ fourth.addElement(4.1);
+ fourth.addElement(4.2);
+ fourth.addElement(4.3);
+ verifyEquality(third, fourth);
+
+ // expand
+ fourth.addElement(4.4);
+ verifyInequality(third, fourth);
+ third.addElement(4.4);
+ verifyEquality(third, fourth);
+ fourth.addElement(4.4);
+ verifyInequality(third, fourth);
+ third.addElement(4.4);
+ verifyEquality(third, fourth);
+ fourth.addElementRolling(4.5);
+ third.addElementRolling(4.5);
+ verifyEquality(third, fourth);
+
+ // discard
+ third.discardFrontElements(1);
+ verifyInequality(third, fourth);
+ fourth.discardFrontElements(1);
+ verifyEquality(third, fourth);
+
+ // discard recent
+ third.discardMostRecentElements(2);
+ fourth.discardMostRecentElements(2);
+ verifyEquality(third, fourth);
+
+ // wrong order
+ third.addElement(18);
+ fourth.addElement(17);
+ third.addElement(17);
+ fourth.addElement(18);
+ verifyInequality(third, fourth);
+
+ // copy
+ ResizableDoubleArray.copy(fourth, fifth);
+ verifyEquality(fourth, fifth);
+
+ // Copy constructor
+ verifyEquality(fourth, new ResizableDoubleArray(fourth));
+
+ // Instance copy
+ verifyEquality(fourth, fourth.copy());
+
+ }
+
+ private void verifyEquality(ResizableDoubleArray a, ResizableDoubleArray b) {
+ assertTrue(b.equals(a));
+ assertTrue(a.equals(b));
+ assertEquals(a.hashCode(), b.hashCode());
+ }
+
+ private void verifyInequality(ResizableDoubleArray a, ResizableDoubleArray b) {
+ assertFalse(b.equals(a));
+ assertFalse(a.equals(b));
+ assertFalse(a.hashCode() == b.hashCode());
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/TestBean.java b/src/test/java/org/apache/commons/math/util/TestBean.java
new file mode 100644
index 0000000..3c8f999
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/TestBean.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.util;
+
+import org.apache.commons.math.MathRuntimeException;
+import org.apache.commons.math.exception.util.LocalizedFormats;
+
+/**
+ * @version $Revision: 983921 $ $Date: 2010-08-10 12:46:06 +0200 (mar. 10 août 2010) $
+ */
+public class TestBean {
+ private Double x = Double.valueOf(1.0);
+
+ private String y = "1.0";
+
+ /**
+ *
+ */
+ public Double getX() {
+ return x;
+ }
+
+ /**
+ *
+ */
+ public String getY() {
+ return y;
+ }
+
+ /**
+ *
+ */
+ public void setX(Double double1) {
+ x = double1;
+ }
+
+ /**
+ *
+ */
+ public void setY(String string) {
+ y = string;
+ }
+
+ /**
+ *
+ */
+ public Double getZ() {
+ throw new MathRuntimeException(LocalizedFormats.SIMPLE_MESSAGE, "?");
+ }
+
+ /**
+ *
+ */
+ public void setZ(Double double1) {
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/math/util/TransformerMapTest.java b/src/test/java/org/apache/commons/math/util/TransformerMapTest.java
new file mode 100644
index 0000000..7c47fcd
--- /dev/null
+++ b/src/test/java/org/apache/commons/math/util/TransformerMapTest.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.util;
+
+import org.apache.commons.math.TestUtils;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
+ */
+public class TransformerMapTest extends TestCase {
+ /**
+ *
+ */
+ public void testPutTransformer(){
+ NumberTransformer expected = new DefaultTransformer();
+
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertEquals(expected, map.getTransformer(TransformerMapTest.class));
+ }
+
+ /**
+ *
+ */
+ public void testContainsClass(){
+ NumberTransformer expected = new DefaultTransformer();
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.containsClass(TransformerMapTest.class));
+ }
+
+ /**
+ *
+ */
+ public void testContainsTransformer(){
+ NumberTransformer expected = new DefaultTransformer();
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.containsTransformer(expected));
+ }
+
+ /**
+ *
+ */
+ public void testRemoveTransformer(){
+ NumberTransformer expected = new DefaultTransformer();
+
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.containsClass(TransformerMapTest.class));
+ assertTrue(map.containsTransformer(expected));
+ map.removeTransformer(TransformerMapTest.class);
+ assertFalse(map.containsClass(TransformerMapTest.class));
+ assertFalse(map.containsTransformer(expected));
+ }
+
+ /**
+ *
+ */
+ public void testClear(){
+ NumberTransformer expected = new DefaultTransformer();
+
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.containsClass(TransformerMapTest.class));
+ map.clear();
+ assertFalse(map.containsClass(TransformerMapTest.class));
+ }
+
+ /**
+ *
+ */
+ public void testClasses(){
+ NumberTransformer expected = new DefaultTransformer();
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.classes().contains(TransformerMapTest.class));
+ }
+
+ /**
+ *
+ */
+ public void testTransformers(){
+ NumberTransformer expected = new DefaultTransformer();
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertTrue(map.transformers().contains(expected));
+ }
+
+ public void testSerial(){
+ NumberTransformer expected = new DefaultTransformer();
+ TransformerMap map = new TransformerMap();
+ map.putTransformer(TransformerMapTest.class, expected);
+ assertEquals(map, TestUtils.serializeAndRecover(map));
+ }
+
+}
diff --git a/src/test/resources/org/apache/commons/math/random/emptyFile.txt b/src/test/resources/org/apache/commons/math/random/emptyFile.txt
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/org/apache/commons/math/random/testData.txt b/src/test/resources/org/apache/commons/math/random/testData.txt
new file mode 100644
index 0000000..4a10132
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/random/testData.txt
@@ -0,0 +1,1000 @@
+4.038625496201205
+3.6485326248346936
+3.6651209675932845
+5.814896279561131
+5.384126469824717
+5.251190723365563
+4.465213440111648
+4.736608014129308
+5.566383814840726
+3.8872277480629114
+5.246598498086048
+3.7511487364188176
+6.733371385175343
+5.388632419618035
+6.036263402962769
+3.8105605069222905
+5.738599503606028
+4.994552792425298
+2.945504336988336
+4.095314381239862
+5.760924710543879
+3.889753944419315
+3.808861160991701
+5.084302950012555
+6.370292933705698
+5.9615431859588455
+4.8790354385481445
+4.068164663649243
+4.26491661935213
+5.911067976258105
+4.1316140545022115
+4.0479985648577115
+5.560425919351912
+5.7777862258090265
+4.6491664757229145
+5.322284766164775
+3.9643060017297818
+3.3374606422520423
+4.070818520139152
+5.162814971692577
+4.68959876937858
+5.729533112912969
+7.160010937980058
+5.154920628387343
+5.992604820701763
+5.317279162752973
+6.3388993007264
+4.38451874656454
+5.024014917973479
+3.7534872319471946
+5.042363342784924
+5.528064915562473
+4.645749024871185
+2.5572755520373756
+3.953716919712825
+3.479482401208564
+4.676100783445583
+4.602236051449888
+7.136692300563229
+3.2411466558895095
+4.188618724984135
+3.6403999184454445
+3.4104206071160776
+4.807963390662261
+4.039073733966207
+4.201017826594899
+4.381868005163936
+5.0635235098658935
+5.9840760229548735
+4.195400406346137
+5.649256144660377
+4.679088153774095
+4.169683379901892
+5.671299804360453
+6.159089864893807
+5.315685219074694
+6.3327786025390305
+5.57348047674858
+6.124073677151904
+4.599177837977919
+4.792320274522308
+5.670142645005109
+5.479531549270221
+4.7740747976996705
+4.99464290442364
+5.474337090086012
+5.232353744280737
+6.411298157619626
+4.757268066271138
+5.217779158748026
+5.07395379944902
+5.5759984176628965
+4.521182520094989
+5.738026445940142
+4.742204968903384
+4.670762511507285
+4.884925361512115
+3.2573282568729462
+4.548387675110013
+4.337950021352034
+3.7875587274144618
+3.6055586455442974
+7.361861332197413
+4.834945622163155
+6.019473054836964
+4.453304225895349
+3.258695681592217
+5.794572588445252
+3.706438851580378
+6.079300672323756
+4.828008457182361
+5.315261102210712
+3.981847695058188
+4.039767325290114
+5.790863349525503
+5.160671471128728
+4.835236459763434
+4.405281184174698
+6.036293520404953
+5.889067983920324
+4.645514887430352
+4.347244670972515
+6.447181244432997
+6.564267268399325
+5.1013992003059885
+4.123378901103389
+2.7101740954226283
+4.209200680057913
+5.085704888132955
+4.26810090240086
+5.54254381015526
+4.721081268239747
+6.890088385585999
+3.9983110493877954
+5.321006894021748
+4.316867375040024
+3.694764624577479
+5.453875921043777
+3.7798857421649927
+3.7228653199742623
+4.807698651287013
+3.953745662132547
+4.821189486606762
+4.453489509051613
+6.4517275696030225
+4.823034188588044
+4.722822481625316
+5.810689805246644
+2.79248319144007
+4.928162269110059
+4.661918219482871
+4.574123379557206
+5.241478194631993
+5.8345087395944155
+7.024415739565572
+3.5536052565954
+6.095523994939967
+5.714650096778455
+4.846741263282074
+6.002769586957791
+5.982356840660745
+5.045480762532407
+6.461077219605347
+4.806649266171423
+6.350848113862498
+6.402679936682352
+3.8190196431210914
+4.189064677946727
+4.868517260374518
+2.145198341547173
+5.9469461091347
+5.88772432287321
+4.258280909990726
+6.740134075161574
+4.6047650031608445
+3.9273659909763774
+4.291244045368331
+5.183827109845055
+5.696810583954774
+3.608472134466666
+4.169004030425733
+3.9965477474540467
+3.7571221568428017
+5.575534565152322
+5.0764208309825065
+5.3185446180363485
+5.157450995663762
+4.961815558094033
+5.687338919107788
+4.185906295178724
+4.382007991332045
+3.5280961455873676
+4.531506809760329
+4.5870808989545635
+4.1932173503939625
+7.213813469684956
+3.1814836225682908
+4.647297462243001
+5.223975935315364
+5.585001659776854
+5.120918864744974
+5.026571594328509
+6.348097968923147
+6.50023470519147
+5.712556147497515
+5.206026515338916
+5.749621140061565
+3.0714726650374033
+6.576852312067237
+7.873101351668455
+6.565410149266118
+6.42923283018875
+4.576910183319347
+4.891822273316748
+6.059357175219146
+3.5324494806223328
+5.02539429500825
+6.078049839679652
+4.395054417468175
+5.710022806162443
+5.577091376894232
+3.131753802875934
+5.4869998928318
+6.413992453090146
+6.368380820674971
+6.052068461844252
+5.480278219624535
+7.051236227914206
+4.748415087916627
+4.559239556696287
+4.780665068784505
+5.349223485326002
+4.522305152002386
+5.678473361027541
+6.734219964637535
+6.713281662687456
+6.22214905332774
+5.716102543806569
+6.336616632829493
+4.8399311234283635
+5.811391244308217
+4.3965755331585905
+5.297963707368242
+5.021726117260926
+4.497125082388555
+4.735667209277485
+6.048804114181307
+4.264048138073914
+7.181013762432201
+4.781684059171574
+5.779533272721499
+4.164421460389599
+3.6986809242837757
+4.8415576143236185
+4.924528568365373
+4.758045335351253
+5.628351489493518
+5.7967639104855415
+4.491988822693669
+2.776750089391839
+4.704679957076673
+4.039607278211126
+5.660350110077993
+4.955611684289963
+3.0641653090331107
+4.896101875253389
+3.6528358436331603
+5.552472713484715
+4.857203367367906
+6.698826102960048
+4.485176970803183
+3.359916665137426
+4.036570806638963
+3.48793689188148
+4.19214761961293
+3.9792199677002866
+6.760873252923419
+4.333561067615548
+5.018626873497648
+3.377671327382937
+4.426183834007672
+8.806961710969402
+5.2068790380550265
+5.483008251803699
+4.287267636533901
+5.931330465014387
+5.257260104496106
+4.623908313559696
+4.365112456604631
+5.600514050451817
+6.184093404453588
+4.9116883675838485
+6.019780977080248
+7.485280872899538
+3.5982660410679284
+4.232210941334369
+5.446496617538108
+6.487976163896015
+3.3960660696641156
+4.733884295853101
+5.352545256764909
+4.107747627715545
+3.949506252011129
+5.017847997679714
+4.24906287118262
+6.720303792581198
+5.832137142236708
+5.010377506040941
+6.458070081692352
+6.501223021355141
+4.612768564431788
+3.801740464538825
+4.469721893125596
+5.061713024524103
+6.872685648715577
+6.145993249521355
+4.638532388190405
+4.70471535512485
+6.417576222531886
+4.118577249932789
+4.431328683019108
+4.747884983644578
+4.495605382683923
+3.5972439735401767
+5.210796056817244
+2.9160129894156026
+3.4596190721900797
+3.972452277026154
+5.5237190584690214
+6.104711796147512
+4.787324556447702
+4.548676032231089
+6.356843891618192
+3.6148998030697816
+4.760679260180754
+4.698041709266617
+4.244003753086054
+5.595546833817678
+3.2784025595193267
+5.326657347561453
+6.213858447402109
+5.213011705084566
+7.232075882741927
+4.806572191866972
+4.680144670146755
+3.946663831660007
+3.6143084085883554
+7.789315918667734
+7.099181638561095
+3.672742516732736
+5.953845998789752
+6.28847712720666
+6.946689084108734
+6.325454389782429
+4.334133006331358
+3.039424552213366
+4.847328734611504
+4.249781519880862
+6.126424188695286
+3.3823135936253257
+6.3280255743100025
+6.150431997847958
+5.4226742397784005
+5.94864601826791
+4.425906835511732
+4.897545227095195
+6.26027333638832
+3.647858418615367
+5.276322437292433
+4.176876069581277
+4.346107259567459
+3.1384418250329995
+4.212957347421948
+4.637757894698186
+6.535589923573854
+5.193072556110316
+5.017309492685374
+5.1750466093509
+4.6381038872450375
+6.071604634493695
+4.357240286904764
+5.122391923621759
+6.556903940099011
+3.8006848201076036
+4.522363890394659
+6.2456602471550635
+5.829300589393535
+4.452647643620209
+5.371890862621703
+4.948677662633424
+5.661113800822228
+5.773629402623548
+6.139823333391851
+6.093004328053013
+5.362399773949667
+6.915042845179306
+5.394739321037944
+5.141451574018252
+5.053294383161769
+4.9834920876470665
+6.812746808763125
+3.5705971688428266
+4.664119854301202
+6.310596552569324
+5.674835228932813
+3.4639740645984807
+4.788956793299906
+5.1005488900135845
+4.534989910256703
+3.931742089464332
+3.572625977535623
+5.374511045697475
+3.859408179493194
+5.767053789854141
+5.1414168750827285
+4.7490168496463525
+7.481142748403815
+4.5189492261011575
+5.40235395980428
+6.700234279658992
+3.5063554778412183
+3.9690452319798735
+3.00630763890251
+7.23611608840341
+5.018006325958164
+4.523410620276403
+4.076684362167451
+5.916234395538267
+7.047286572236027
+3.8682363461132017
+4.390658924201581
+4.5292330092964255
+5.07906584568947
+4.671213490610071
+4.095193931403399
+4.054590162572947
+3.2227278245030027
+6.132646335444107
+2.8407359953623814
+4.7279370282096655
+5.593872406613741
+3.382542536766184
+5.85844025043303
+6.461000354065181
+3.4994741020969773
+4.132683344034104
+5.647894883473891
+5.011301190267978
+4.401435886120444
+3.3496957519609927
+6.119687677370172
+4.644762759286699
+4.5205629205178735
+3.0320051244977195
+4.596487037061894
+5.14520534308978
+5.282269168918912
+5.761372455401502
+4.148416743583162
+6.372742039103559
+5.649143130777574
+5.084494528193606
+4.811163551671385
+3.9806520282362476
+4.411511792047385
+4.670987670787611
+5.768451736319585
+3.984558689428816
+5.3293696591099975
+5.413539058295544
+6.40970782426591
+5.481145473625602
+4.36515208836978
+5.161811987273001
+3.963554978392394
+5.098946908474979
+7.786683053797615
+4.927631219070586
+5.524180021898693
+4.523736107490982
+3.557364094609177
+6.128701594561169
+4.2509207146594115
+3.944944115965259
+4.966138264389299
+5.394430219583224
+6.77531735530901
+4.128069102169693
+5.2683457909620355
+3.8872836447608496
+4.486696800422189
+6.5335585640393825
+4.916400608546338
+4.270979919569207
+4.311416898242187
+4.498167295277512
+6.132808180917634
+5.1041291367018875
+5.498388642491546
+5.584526454067219
+6.142894025331306
+4.944671156061267
+4.1686843376349945
+4.818977261651865
+6.235820918635881
+3.3212806573760028
+5.68435151611855
+6.189749316228399
+3.591267557367338
+5.043902793214377
+7.818905185451641
+4.768708643560666
+5.669288800286096
+6.398657810380692
+5.3717200778027605
+5.2573487416126525
+4.822935237131512
+6.182572962936934
+5.6072955002277105
+3.9675191626756288
+6.350858167126948
+6.283995295688788
+4.445782391191543
+6.877548745307459
+5.3208290700871315
+6.09847688940267
+6.434994026138841
+4.32779758193763
+7.2924037238697
+5.419935895280957
+4.288818201810987
+7.242433265647824
+4.947890367713541
+5.916218606455959
+6.490437527083841
+4.617582424838291
+7.957708355752131
+4.879357620439287
+6.103294400805588
+5.639488259504568
+4.335236791293937
+5.202542624850618
+5.4406339076225505
+5.782530244910674
+4.055314639567904
+5.552293301411749
+5.290496801505254
+4.022580394801182
+4.625571974654451
+5.5086593656510825
+4.913637297182931
+4.906396844626936
+6.439485089212817
+5.7942799739945325
+7.158136207286507
+4.280431104751667
+3.9206066719991517
+5.127791240556268
+6.70098532482022
+4.657147097255419
+4.524267698037553
+4.647534545829241
+4.839690189371444
+6.798322548455047
+5.094754599613737
+5.916399329150566
+5.767837713902285
+5.294550523894544
+4.161295164684424
+5.233358678928891
+5.546871474458429
+4.897048191655597
+3.939430251326603
+5.005888208270397
+3.2926576330038655
+4.0159694347757835
+5.056229917378723
+6.568879235955665
+4.497327615853924
+4.690014685240942
+4.746884105330737
+4.841384111334085
+4.14796180246966
+5.461902744235217
+5.869304766250897
+3.52354738655413
+5.582741221891035
+4.997825621424692
+5.439611672191855
+4.819402835865619
+5.76136287301575
+6.143090288547951
+5.976125217642891
+6.157007787875113
+4.912778652906766
+6.540414953620538
+3.8210262932626495
+4.727149320768898
+4.955255599543759
+5.7983414047818265
+5.167409288825197
+5.059246623397723
+6.965380962189423
+5.531311904089661
+5.4022568784996885
+4.344352255655229
+5.745261070226892
+5.118820012265567
+4.960430609470355
+4.487905086804239
+3.8537512154805835
+4.839114062528739
+5.367538410451759
+6.202050661574205
+4.001800559371117
+6.119617239220475
+3.236283913097008
+5.610134770285298
+5.757041556538514
+4.083399027093518
+5.055588718117847
+4.580930359877383
+6.545516697552579
+5.916270431823864
+3.761559453909257
+6.037777237143994
+7.29718541816528
+4.8965176227762734
+3.941358569293476
+3.9289815988008847
+3.2604315357316436
+4.639329221347256
+6.570997662310685
+3.851958625190621
+5.859087244914328
+4.647365626452129
+6.076778087850363
+4.627936340272149
+4.422345848512504
+6.2183675417422
+5.243889853389288
+5.90909311946919
+6.09260484846961
+6.0271781583360475
+6.913810502971691
+5.285845705409185
+5.318460367681083
+5.179580543035928
+4.6834977896331615
+5.382546996207003
+4.606307320228796
+4.038858683454586
+6.271279252908354
+6.0668723017439365
+5.713564644555386
+5.144428649779485
+5.2496039700779615
+3.8392027391676207
+4.7050632415088876
+7.137275712725372
+4.208879180827962
+4.81480733241727
+4.699941077536472
+4.423440083005291
+5.742161495602944
+4.592506326637019
+6.224046541049566
+4.611093653533141
+6.1166037746546165
+5.904004955760586
+5.589433336321981
+4.57489510266225
+5.500028469975376
+4.382479722617695
+4.257470376496386
+6.373209588018213
+5.375461447759072
+2.8662337475774584
+4.699832117278568
+3.102810935311515
+6.501460955698313
+4.550333534073201
+7.944860661426514
+5.69020177364993
+4.006199611798767
+5.11813341012065
+4.896479097282944
+4.816212778128475
+4.940296064591277
+5.419056166663748
+3.4659391357743616
+7.246324501631043
+5.907112751874067
+5.614502217435809
+4.750614593197476
+7.0951293897280205
+4.3819944682346055
+4.958360318480322
+4.962367933857186
+5.715499159595656
+5.220101872658552
+6.088622700649866
+5.491586360474799
+4.656477235994459
+3.8695533953710326
+3.7143742249025538
+3.7411936672155304
+6.603415889834424
+5.62928670505705
+5.5959396553858785
+5.6577330557176095
+6.003846653929077
+4.508563176717718
+5.549881486113916
+4.953305865372426
+6.203554032873964
+5.612208234244517
+4.854464793588011
+5.263585016371758
+3.897600440182904
+5.981235398882815
+5.531277293213279
+4.8817070690071445
+3.712544699529063
+3.513432242611217
+5.006035295792077
+7.124520658535316
+3.4782033127607037
+4.829578059223972
+5.742892584711905
+4.361333503197903
+4.601687049512891
+6.035189727259647
+4.711273209758127
+4.272043208125593
+4.447702393976457
+5.17487393756583
+4.741015989802225
+4.953808452791662
+4.6645084492292765
+4.276788530554644
+7.325515154525428
+4.602597440231014
+5.082884146093998
+3.068409439905545
+4.809983425115099
+3.8747882947708083
+4.893233436073575
+5.376932606908371
+6.239910432522629
+6.041695571547008
+5.317735375674715
+5.160517819092331
+5.283748111202239
+6.5357867130743745
+5.537247902605441
+5.4185896683530235
+5.287616337544387
+5.981700012459223
+5.992385624329782
+5.758772999982491
+4.599744432168506
+5.7237660286844605
+2.5862937961454855
+4.319918124665613
+7.566860260437548
+3.202784785619934
+6.67642720284947
+5.215802050091852
+5.452814592454087
+4.192858032386887
+5.299199379721475
+3.291677765243241
+4.632695766333648
+5.115714853147839
+4.996260485718097
+3.5271286032511773
+4.659715887897552
+6.587392147323261
+5.989132075359954
+3.8378063660060056
+4.975951043892332
+3.90853196371359
+5.708783809093124
+6.591895462100242
+5.653528117636727
+3.665428787393319
+4.324537690925271
+6.234413976864244
+4.053504794002944
+5.713371183460703
+4.670243561862966
+3.352660528859447
+4.020147292531281
+5.121933145078237
+4.282377411958472
+4.088770874857499
+4.275716553910016
+4.284046155337823
+6.449567142111275
+3.3275914286077084
+4.837717853228399
+5.261182985672333
+6.073443097165901
+5.40483608136289
+4.690566013556853
+4.222184746341714
+5.790245443382679
+5.020060832906476
+5.576527321711127
+5.340393035828579
+5.301460661931292
+5.076040366457228
+6.296482877500045
+3.037720796600903
+6.321850760102656
+5.701339165316606
+4.991940459105436
+5.758970102557518
+4.322111367356909
+5.721255109646473
+5.511881303620453
+4.9563635195228954
+6.861001584068987
+3.8299029968884195
+4.322974731453332
+5.3047403550360634
+6.0756269754391825
+6.117153630436378
+4.5085862451026495
+4.832132638553977
+4.699215334058029
+7.982648077178181
+3.303778194960711
+6.845166964779691
+5.175136241842978
+5.611538016661082
+4.293354218279116
+6.2617605857039775
+4.646868778200023
+5.596211970851805
+6.4731028962866635
+5.9737535333484795
+6.411386536458501
+2.7695062051965302
+3.5560570906765894
+5.451690061978083
+6.503535887841675
+4.695530301460264
+4.706120568510652
+2.800841111510871
+5.364729318170148
+5.1911558656154835
+5.947415408072919
+4.777513714112934
+4.596459418828304
+5.043317097051506
+5.174749896541634
+5.258257882159918
+3.887023257269741
+5.131383317673293
+5.843231353166214
+6.472487193651527
+5.763704927517821
+6.024396779444038
+4.926879229092987
+6.558645082464584
+5.447575064546803
+4.286751335276036
+3.9071252303818644
+4.618489035299945
+5.088217807208579
+3.808752600228301
+5.861810119867259
+4.033532296400091
+5.74542761207288
+4.925806147050348
+3.679404591586196
+4.05604604887352
+5.87881882930846
+4.513573760688276
+4.915009783906388
+3.654483449601882
+4.912095784340134
+3.3774256594506396
+4.188548007093734
+5.4860540834510445
+4.483111427918742
+6.091604204270534
+4.913639044459108
+6.347957296069254
+5.777137740280461
+4.996625717628335
+3.357832000765961
+4.529640780144531
+6.655383658310578
+5.187418414545693
+5.275067584707507
+5.50723064248028
+4.636201988408981
+4.947416066568987
+7.027581910469225
+4.570962245627946
+5.947355941474328
+4.7057667163042245
+4.786943520378938
+5.615852784022176
+4.645129057815488
+5.263882354785195
+3.844951724466573
+5.554260404852657
+4.684091248268045
+5.13336102667963
+3.417837773686996
+4.392489033666552
+6.270027300253521
+6.102372796945901
+4.219653651099504
+5.076173402237902
+4.383422445264855
+3.0437995085361025
+5.377941796580727
+6.276975902314367
+4.315133675763909
+5.507204150696545
+4.886780791403244
+7.147240935203286
+3.900457465197911
+5.102470142455588
+7.084247234995372
+5.457300111792919
+4.60867925423519
+6.2840312118540815
+7.236947706509271
+7.133509547170027
+4.3015318378968
+5.043756433592529
+5.108881706267525
+5.5240023845728645
+5.858632364389344
+5.981971317600007
+6.259948473084726
+4.062783955426871
+5.218852203995356
+3.8038254404258813
+4.758585778361602
+4.376196481713867
+4.458880802424765
+3.96326498727664
+3.6778134622710104
+4.374934998721925
+7.489468914416122
+5.700987063590436
+3.3100952240676955
+5.1696122166092415
+6.541584919841012
+4.4595571152023465
+4.366611842258099
+5.382259676070623
+4.7794428978336825
+3.757838857759169
+6.545307984939696
+4.881890171568036
+5.7063933726311165
+4.7730257133517116
+3.873677842944983
+3.840259191338565
+4.593661080441791
+4.511107632929962
+5.5385052402039605
+5.441167937479936
+5.984890322415174
+5.403820054129332
+5.148546201719365
+4.838476271562129
+6.2440438844133075
+3.9741885421050913
+6.327490860577795
+4.633940514497735
+5.232122748521683
+4.456999940494487
+5.576626928088951
+3.2818610857426584
+5.134684374559793
+4.602466559265273
+5.891324885962796
+5.517816321593768
+6.624687761337339
+5.2683180340267874
+4.662418552035468
+4.622236368091395
+5.536060664096081
+3.272870360657461
+3.9899131914173696
+5.121549579739896
+5.928806028927443
+4.259133981719825
+5.313734011651727
+5.635277610987355
+4.524627655490917
diff --git a/src/test/resources/org/apache/commons/math/stat/data/Lew.txt b/src/test/resources/org/apache/commons/math/stat/data/Lew.txt
new file mode 100644
index 0000000..2474560
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/Lew.txt
@@ -0,0 +1,252 @@
+#####################################################################
+# Dataset Name: Lew (Beam Deflection Data)
+#
+# Description: This is an observed/"real world" data set
+# consisting of 200 deflections of a steel-concrete
+# beam while subjected to periodic pressure.
+# The experimenter was H. S. Lew of the
+# Center for Building Technology at NIST.
+# We here use this data to test accuracy
+# in summary statistics calculations.
+#
+# Stat Category: Univariate: Summary Statistics
+#
+# Reference: http://www.itl.nist.gov/div898/strd/univ/lew.html
+#
+# Data: "Real World"
+# 1 Response : y = beam deflection
+# 0 Predictors
+# 200 Observations
+#
+# Model: Lower Level of Difficulty
+# 2 Parameters : mu, sigma
+# 1 Response Variable : y
+# 0 Predictor Variables
+# y = mu + e
+#####################################################################
+
+#####################################################################
+#
+# Certified Values
+#
+#####################################################################
+n = 200
+mean = -177.435000000000
+standardDeviation = 277.332168044316
+autocorrelationCoefficient = -0.307304800605679
+
+#####################################################################
+#
+# R Generated Values
+#
+#####################################################################
+variance = 76913.13143
+max = 300
+min = -579
+sum = -35487
+
+#####################################################################
+#
+# Data
+#
+#####################################################################
+ -213
+ -564
+ -35
+ -15
+ 141
+ 115
+ -420
+ -360
+ 203
+ -338
+ -431
+ 194
+ -220
+ -513
+ 154
+ -125
+ -559
+ 92
+ -21
+ -579
+ -52
+ 99
+ -543
+ -175
+ 162
+ -457
+ -346
+ 204
+ -300
+ -474
+ 164
+ -107
+ -572
+ -8
+ 83
+ -541
+ -224
+ 180
+ -420
+ -374
+ 201
+ -236
+ -531
+ 83
+ 27
+ -564
+ -112
+ 131
+ -507
+ -254
+ 199
+ -311
+ -495
+ 143
+ -46
+ -579
+ -90
+ 136
+ -472
+ -338
+ 202
+ -287
+ -477
+ 169
+ -124
+ -568
+ 17
+ 48
+ -568
+ -135
+ 162
+ -430
+ -422
+ 172
+ -74
+ -577
+ -13
+ 92
+ -534
+ -243
+ 194
+ -355
+ -465
+ 156
+ -81
+ -578
+ -64
+ 139
+ -449
+ -384
+ 193
+ -198
+ -538
+ 110
+ -44
+ -577
+ -6
+ 66
+ -552
+ -164
+ 161
+ -460
+ -344
+ 205
+ -281
+ -504
+ 134
+ -28
+ -576
+ -118
+ 156
+ -437
+ -381
+ 200
+ -220
+ -540
+ 83
+ 11
+ -568
+ -160
+ 172
+ -414
+ -408
+ 188
+ -125
+ -572
+ -32
+ 139
+ -492
+ -321
+ 205
+ -262
+ -504
+ 142
+ -83
+ -574
+ 0
+ 48
+ -571
+ -106
+ 137
+ -501
+ -266
+ 190
+ -391
+ -406
+ 194
+ -186
+ -553
+ 83
+ -13
+ -577
+ -49
+ 103
+ -515
+ -280
+ 201
+ 300
+ -506
+ 131
+ -45
+ -578
+ -80
+ 138
+ -462
+ -361
+ 201
+ -211
+ -554
+ 32
+ 74
+ -533
+ -235
+ 187
+ -372
+ -442
+ 182
+ -147
+ -566
+ 25
+ 68
+ -535
+ -244
+ 194
+ -351
+ -463
+ 174
+ -125
+ -570
+ 15
+ 72
+ -550
+ -190
+ 172
+ -424
+ -385
+ 198
+ -218
+ -536
+ 96
diff --git a/src/test/resources/org/apache/commons/math/stat/data/Lottery.txt b/src/test/resources/org/apache/commons/math/stat/data/Lottery.txt
new file mode 100644
index 0000000..ef5e931
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/Lottery.txt
@@ -0,0 +1,261 @@
+#####################################################################
+# Dataset Name: Lottery
+#
+#Description: This is an observed/"real world" data set
+# consisting of 218 lottery values
+# from September 3, 1989 to April 14, 1990 (32 weeks).
+# One 3-digit random number (from 000 to 999)
+# is drawn per day, 7 days per week for most
+# weeks, but fewer days per week for some weeks.
+# We here use this data to test accuracy
+# in summary statistics calculations.
+#
+# Stat Category: Univariate: Summary Statistics
+#
+# Reference: http://www.itl.nist.gov/div898/strd/univ/lottery.html
+#
+# Data: "Real World"
+# 1 Response : y = 3-digit random number
+# 0 Predictors
+# 218 Observations
+#
+# Model: Lower Level of Difficulty
+# 2 Parameters : mu, sigma
+# 1 Response Variable : y
+# 0 Predictor Variables#
+# y = mu + e
+#####################################################################
+
+#####################################################################
+#
+# Certified Values
+#
+#####################################################################
+mean = 518.958715596330
+standardDeviation = 291.699727470969
+autocorrelationCoefficient = -0.120948622967393
+n = 218
+
+#####################################################################
+#
+# Data
+#
+#####################################################################
+ 162
+ 671
+ 933
+ 414
+ 788
+ 730
+ 817
+ 33
+ 536
+ 875
+ 670
+ 236
+ 473
+ 167
+ 877
+ 980
+ 316
+ 950
+ 456
+ 92
+ 517
+ 557
+ 956
+ 954
+ 104
+ 178
+ 794
+ 278
+ 147
+ 773
+ 437
+ 435
+ 502
+ 610
+ 582
+ 780
+ 689
+ 562
+ 964
+ 791
+ 28
+ 97
+ 848
+ 281
+ 858
+ 538
+ 660
+ 972
+ 671
+ 613
+ 867
+ 448
+ 738
+ 966
+ 139
+ 636
+ 847
+ 659
+ 754
+ 243
+ 122
+ 455
+ 195
+ 968
+ 793
+ 59
+ 730
+ 361
+ 574
+ 522
+ 97
+ 762
+ 431
+ 158
+ 429
+ 414
+ 22
+ 629
+ 788
+ 999
+ 187
+ 215
+ 810
+ 782
+ 47
+ 34
+ 108
+ 986
+ 25
+ 644
+ 829
+ 630
+ 315
+ 567
+ 919
+ 331
+ 207
+ 412
+ 242
+ 607
+ 668
+ 944
+ 749
+ 168
+ 864
+ 442
+ 533
+ 805
+ 372
+ 63
+ 458
+ 777
+ 416
+ 340
+ 436
+ 140
+ 919
+ 350
+ 510
+ 572
+ 905
+ 900
+ 85
+ 389
+ 473
+ 758
+ 444
+ 169
+ 625
+ 692
+ 140
+ 897
+ 672
+ 288
+ 312
+ 860
+ 724
+ 226
+ 884
+ 508
+ 976
+ 741
+ 476
+ 417
+ 831
+ 15
+ 318
+ 432
+ 241
+ 114
+ 799
+ 955
+ 833
+ 358
+ 935
+ 146
+ 630
+ 830
+ 440
+ 642
+ 356
+ 373
+ 271
+ 715
+ 367
+ 393
+ 190
+ 669
+ 8
+ 861
+ 108
+ 795
+ 269
+ 590
+ 326
+ 866
+ 64
+ 523
+ 862
+ 840
+ 219
+ 382
+ 998
+ 4
+ 628
+ 305
+ 747
+ 247
+ 34
+ 747
+ 729
+ 645
+ 856
+ 974
+ 24
+ 568
+ 24
+ 694
+ 608
+ 480
+ 410
+ 729
+ 947
+ 293
+ 53
+ 930
+ 223
+ 203
+ 677
+ 227
+ 62
+ 455
+ 387
+ 318
+ 562
+ 242
+ 428
+ 968
diff --git a/src/test/resources/org/apache/commons/math/stat/data/Mavro.txt b/src/test/resources/org/apache/commons/math/stat/data/Mavro.txt
new file mode 100644
index 0000000..a3f7c7a
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/Mavro.txt
@@ -0,0 +1,110 @@
+File Name: Mavro.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 110 (= 50)
+
+Dataset Name: Mavro (Filter Transmittance Data)
+
+Description: This is an observed/"real world" data set
+ consisting of 50 transmittance measurements
+ (at a sampling rate of 10 observations per second)
+ from a filter with a nominal value of 2.
+ The experimenter was Radu Mavrodineaunu,
+ a member of the chemistry staff at NIST.
+ We here use this data to test accuracy
+ in summary statistics calculations.
+
+Stat Category: Univariate: Summary Statistics
+
+Reference: None
+
+Data: "Real World"
+ 1 Response : y = transmittance
+ 0 Predictors
+ 50 Observations
+
+Model: Lower Level of Difficulty
+ 2 Parameters : mu, sigma
+ 1 Response Variable : y
+ 0 Predictor Variables
+
+ y = mu + e
+
+
+
+
+
+
+ Certified Values
+Sample Mean ybar: 2.00185600000000
+Sample Standard Deviation (denom. = n-1) s: 0.000429123454003053
+Sample Autocorrelation Coefficient (lag 1) r(1): 0.937989183438248
+
+Number of Observations: 50
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+-------------
+ 2.00180
+ 2.00170
+ 2.00180
+ 2.00190
+ 2.00180
+ 2.00170
+ 2.00150
+ 2.00140
+ 2.00150
+ 2.00150
+ 2.00170
+ 2.00180
+ 2.00180
+ 2.00190
+ 2.00190
+ 2.00210
+ 2.00200
+ 2.00160
+ 2.00140
+ 2.00130
+ 2.00130
+ 2.00150
+ 2.00150
+ 2.00160
+ 2.00150
+ 2.00140
+ 2.00130
+ 2.00140
+ 2.00150
+ 2.00140
+ 2.00150
+ 2.00160
+ 2.00150
+ 2.00160
+ 2.00190
+ 2.00200
+ 2.00200
+ 2.00210
+ 2.00220
+ 2.00230
+ 2.00240
+ 2.00250
+ 2.00270
+ 2.00260
+ 2.00260
+ 2.00260
+ 2.00270
+ 2.00260
+ 2.00250
+ 2.00240
diff --git a/src/test/resources/org/apache/commons/math/stat/data/Michelso.txt b/src/test/resources/org/apache/commons/math/stat/data/Michelso.txt
new file mode 100644
index 0000000..91322cd
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/Michelso.txt
@@ -0,0 +1,160 @@
+File Name: Michelso.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 160 (= 100)
+
+Dataset Name: Michelso (Speed of Light Data, in millions of meters per second)
+
+Description: This is an observed/"real world" data set
+ consisting of 100 measurements of the
+ speed of light in air. This classic experiment
+ was carried out by Michelson is 1879.
+ We here use this data to test accuracy
+ in summary statistics calculations.
+
+Stat Category: Univariate: Summary Statistics
+
+Reference: Dorsey, Ernest N. (1944). The Velocity of Light.
+ Transactions of the American Philiosophical
+ Society, Volume 34, Part 1, Pages 1-110, Table 22.
+
+ y = mu + e
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Certified Values
+Sample Mean ybar: 299.852400000000
+Sample Standard Deviation (denom. = n-1) s: 0.0790105478190518
+Sample Autocorrelation Coefficient (lag 1) r(1): 0.535199668621283
+
+Number of Observations: 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+----------
+ 299.85
+ 299.74
+ 299.90
+ 300.07
+ 299.93
+ 299.85
+ 299.95
+ 299.98
+ 299.98
+ 299.88
+ 300.00
+ 299.98
+ 299.93
+ 299.65
+ 299.76
+ 299.81
+ 300.00
+ 300.00
+ 299.96
+ 299.96
+ 299.96
+ 299.94
+ 299.96
+ 299.94
+ 299.88
+ 299.80
+ 299.85
+ 299.88
+ 299.90
+ 299.84
+ 299.83
+ 299.79
+ 299.81
+ 299.88
+ 299.88
+ 299.83
+ 299.80
+ 299.79
+ 299.76
+ 299.80
+ 299.88
+ 299.88
+ 299.88
+ 299.86
+ 299.72
+ 299.72
+ 299.62
+ 299.86
+ 299.97
+ 299.95
+ 299.88
+ 299.91
+ 299.85
+ 299.87
+ 299.84
+ 299.84
+ 299.85
+ 299.84
+ 299.84
+ 299.84
+ 299.89
+ 299.81
+ 299.81
+ 299.82
+ 299.80
+ 299.77
+ 299.76
+ 299.74
+ 299.75
+ 299.76
+ 299.91
+ 299.92
+ 299.89
+ 299.86
+ 299.88
+ 299.72
+ 299.84
+ 299.85
+ 299.85
+ 299.78
+ 299.89
+ 299.84
+ 299.78
+ 299.81
+ 299.76
+ 299.81
+ 299.79
+ 299.81
+ 299.82
+ 299.85
+ 299.87
+ 299.87
+ 299.81
+ 299.74
+ 299.81
+ 299.94
+ 299.95
+ 299.80
+ 299.81
+ 299.87
diff --git a/src/test/resources/org/apache/commons/math/stat/data/NumAcc1.txt b/src/test/resources/org/apache/commons/math/stat/data/NumAcc1.txt
new file mode 100644
index 0000000..b7a368e
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/NumAcc1.txt
@@ -0,0 +1,63 @@
+File Name: NumAcc1.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 63 (= 3)
+
+Dataset Name: NumAcc1
+
+Description: This is a constructed/fabricated data set
+ to test accuracy in summary statistic calculations.
+ The numbers are large (8-digit integers) and
+ differ only in the last decimal place.
+ Note--by construction, this data set has
+ sample mean = 10000002 (exact)
+ sample standard deviation = 1 (exact)
+ sample autocorrelation coef. = -0.5 (exact)
+
+Stat Category: Univariate: Summary Statistics
+
+Reference: Simon, Stephen D. and Lesage, James P. (1989).
+ Assessing the Accuracy of ANOVA Caluclations
+ in Statistical Software", Computational
+ Statistics & data Analysis, 8, pp. 325-332.
+
+Data: Constructed
+ 1 Response : y
+ 0 Predictors
+ 3 Observations
+
+Model: Lower Level of Difficulty
+ 2 Parameters : mu, sigma
+ 1 Response Variable : y
+ 0 Predictor Variables
+
+ y = mu + e
+
+
+
+ Certified Values
+Sample Mean ybar: 10000002
+Sample Standard Deviation (denom. = n-1) s: 1
+Sample Autocorrelation Coefficient (lag 1) r(1): -0.5
+
+Number of Observations: 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+---------
+10000001
+10000003
+10000002
diff --git a/src/test/resources/org/apache/commons/math/stat/data/NumAcc2.txt b/src/test/resources/org/apache/commons/math/stat/data/NumAcc2.txt
new file mode 100644
index 0000000..0efde02
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/NumAcc2.txt
@@ -0,0 +1,1061 @@
+File Name: NumAcc2.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 1061 (= 1001)
+
+Dataset Name: NumAcc2
+
+Description: This is a constructed/fabricated data set
+ to test accuracy in summary statistic calculations.
+ The numbers are 2-digit floating point values and
+ differ only in the last decimal place.
+ Note--by construction, this data set has
+ sample mean = 1.2 (exact)
+ sample standard deviation = 0.1 (exact)
+ sample autocorrelation coef. = -0.999 (exact)
+
+Stat Category: Univariate
+
+Reference: Simon, Stephen D. and Lesage, James P. (1989).
+ Assessing the Accuracy of ANOVA Caluclations
+ in Statistical Software", Computational
+ Statistics & data Analysis, 8, pp. 325-332.
+
+
+Data: Constructed
+ 1 Response : y
+ 0 Predictors
+ 1001 Observations
+
+Model: Average Level of Difficulty
+ 2 Parameters : mu, sigma
+ 1 Response Variable : y
+ 0 Predictor Variables
+
+ y = mu + e
+
+
+ Certified Values
+Sample Mean ybar: 1.2
+Sample Standard Deviation (denom. = n-1) s: 0.1
+Sample Autocorrelation Coefficient (lag 1) r(1): -0.999
+
+Number of Observations: 1001
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+---------
+ 1.2
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
+ 1.1
+ 1.3
diff --git a/src/test/resources/org/apache/commons/math/stat/data/NumAcc3.txt b/src/test/resources/org/apache/commons/math/stat/data/NumAcc3.txt
new file mode 100644
index 0000000..292d8e0
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/NumAcc3.txt
@@ -0,0 +1,1061 @@
+File Name: NumAcc3.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 1061 (= 1001)
+
+Dataset Name: NumAcc3
+
+Description: This is a constructed/fabricated data set
+ to test accuracy in summary statistic calculations.
+ The numbers are 8-digit floating point values and
+ differ only in the last decimal place.
+ Note--by construction, this data set has
+ sample mean = 1000000.2 (exact)
+ sample standard dev. = 0.1 (exact)
+ sample autocorr. coef. = -0.999 (exact)
+
+Stat Category: Univariate: Summary Statistics
+
+Reference: Simon, Stephen D. and Lesage, James P. (1989).
+ Assessing the Accuracy of ANOVA Caluclations
+ in Statistical Software", Computational
+ Statistics & data Analysis, 8, pp. 325-332.
+
+
+Data: Constructed
+ 1 Response : y
+ 0 Predictors
+ 1001 Observations
+
+Model: Average Level of Difficulty
+ 2 Parameters : mu, sigma
+ 1 Response Variable : y
+ 0 Predictor Variables
+
+ y = mu + e
+
+
+ Certified Values
+Sample Mean ybar: 1000000.2
+Sample Standard Deviation (denom. = n-1) s: 0.1
+Sample Autocorrelation Coefficient (lag 1) r(1): -0.999
+
+Number of Observations: 1001
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+-------------
+ 1000000.2
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
+ 1000000.1
+ 1000000.3
diff --git a/src/test/resources/org/apache/commons/math/stat/data/NumAcc4.txt b/src/test/resources/org/apache/commons/math/stat/data/NumAcc4.txt
new file mode 100644
index 0000000..382c040
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/NumAcc4.txt
@@ -0,0 +1,1061 @@
+File Name: NumAcc4.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 1061 (= 1001)
+
+Dataset Name: NumAcc4
+
+Description: This is a constructed/fabricated data set
+ to test accuracy in summary statistic calculations.
+ The numbers are 9-digit floating point values and
+ differ only in the last decimal place.
+ sample mean = 10000000.2 (exact)
+ sample standard dev. = 0.1 (exact)
+ sample autocorr. coef. = -0.999 (exact)
+
+Stat Category: Univariate
+
+Reference: Simon, Stephen D. and Lesage, James P. (1989).
+ Assessing the Accuracy of ANOVA Caluclations
+ in Statistical Software", Computational
+ Statistics & data Analysis, 8, pp. 325-332.
+
+Data: Constructed
+ 1 Response : y
+ 0 Predictors
+ 1001 Observations
+
+Model: Higher Level of Difficulty
+ 2 Parameters : mu, sigma
+ 1 Response Variable : y
+ 0 Predictor Variables
+
+ y = mu + e
+
+
+
+
+ Certified Values
+Sample Mean ybar: 10000000.2
+Sample Standard Deviation (denom. = n-1) s: 0.1
+Sample Autocorrelation Coefficient (lag 1) r(1): -0.999
+
+Number of Observations: 1001
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+--------------
+ 10000000.2
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
+ 10000000.1
+ 10000000.3
diff --git a/src/test/resources/org/apache/commons/math/stat/data/PiDigits.txt b/src/test/resources/org/apache/commons/math/stat/data/PiDigits.txt
new file mode 100644
index 0000000..1e407b6
--- /dev/null
+++ b/src/test/resources/org/apache/commons/math/stat/data/PiDigits.txt
@@ -0,0 +1,5060 @@
+File Name: PiDigits.dat
+
+File Format: ASCII
+ Header : lines 1 to 60 (= 60)
+ Certified Values: lines 41 to 43 (= 3)
+ Data : lines 61 to 5060 (= 5000)
+
+Dataset Name: PiDigits
+
+Description: This is a constructed/fabricated data set
+ to test accuracy in summary statistic calculations.
+ The numbers are the first 5000 digits of the
+ mathematical constant pi (= 3.1415926535897932384...).
+
+Stat Category: Univariate
+
+Reference: Mathematics of Computation.
+ January 1962, page 76.
+
+Data: Constructed Variable
+ --> 1 Response : y = pi digits
+ --> 0 Predictors
+ --> 5000 Observations
+
+Model: Lower Level of Difficulty
+ --> 2 Parameters : mu, sigma
+ --> 1 Response Variable : y
+ --> 0 Predictor Variables
+
+ y = mu + e
+
+
+
+
+
+
+
+
+
+ Certified Values
+Sample Mean ybar: 4.53480000000000
+Sample Standard Deviation (denom. = n-1) s: 2.86733906028871
+Sample Autocorrelation Coefficient (lag 1) r(1): -0.00355099287237972
+
+Number of Observations: 5000
+
+
+
+
+
+
+
+
+
+
+
+
+
+Data: Y
+---------
+ 3
+ 1
+ 4
+ 1
+ 5
+ 9
+ 2
+ 6
+ 5
+ 3
+ 5
+ 8
+ 9
+ 7
+ 9
+ 3
+ 2
+ 3
+ 8
+ 4
+ 6
+ 2
+ 6
+ 4
+ 3
+ 3
+ 8
+ 3
+ 2
+ 7
+ 9
+ 5
+ 0
+ 2
+ 8
+ 8
+ 4
+ 1
+ 9
+ 7
+ 1
+ 6
+ 9
+ 3
+ 9
+ 9
+ 3
+ 7
+ 5
+ 1
+ 0
+ 5
+ 8
+ 2
+ 0
+ 9
+ 7
+ 4
+ 9
+ 4
+ 4
+ 5
+ 9
+ 2
+ 3
+ 0
+ 7
+ 8
+ 1
+ 6
+ 4
+ 0
+ 6
+ 2
+ 8
+ 6
+ 2
+ 0
+ 8
+ 9
+ 9
+ 8
+ 6
+ 2
+ 8
+ 0
+ 3
+ 4
+ 8
+ 2
+ 5
+ 3
+ 4
+ 2
+ 1
+ 1
+ 7
+ 0
+ 6
+ 7
+ 9
+ 8
+ 2
+ 1
+ 4
+ 8
+ 0
+ 8
+ 6
+ 5
+ 1
+ 3
+ 2
+ 8
+ 2
+ 3
+ 0
+ 6
+ 6
+ 4
+ 7
+ 0
+ 9
+ 3
+ 8
+ 4
+ 4
+ 6
+ 0
+ 9
+ 5
+ 5
+ 0
+ 5
+ 8
+ 2
+ 2
+ 3
+ 1
+ 7
+ 2
+ 5
+ 3
+ 5
+ 9
+ 4
+ 0
+ 8
+ 1
+ 2
+ 8
+ 4
+ 8
+ 1
+ 1
+ 1
+ 7
+ 4
+ 5
+ 0
+ 2
+ 8
+ 4
+ 1
+ 0
+ 2
+ 7
+ 0
+ 1
+ 9
+ 3
+ 8
+ 5
+ 2
+ 1
+ 1
+ 0
+ 5
+ 5
+ 5
+ 9
+ 6
+ 4
+ 4
+ 6
+ 2
+ 2
+ 9
+ 4
+ 8
+ 9
+ 5
+ 4
+ 9
+ 3
+ 0
+ 3
+ 8
+ 1
+ 9
+ 6
+ 4
+ 4
+ 2
+ 8
+ 8
+ 1
+ 0
+ 9
+ 7
+ 5
+ 6
+ 6
+ 5
+ 9
+ 3
+ 3
+ 4
+ 4
+ 6
+ 1
+ 2
+ 8
+ 4
+ 7
+ 5
+ 6
+ 4
+ 8
+ 2
+ 3
+ 3
+ 7
+ 8
+ 6
+ 7
+ 8
+ 3
+ 1
+ 6
+ 5
+ 2
+ 7
+ 1
+ 2
+ 0
+ 1
+ 9
+ 0
+ 9
+ 1
+ 4
+ 5
+ 6
+ 4
+ 8
+ 5
+ 6
+ 6
+ 9
+ 2
+ 3
+ 4
+ 6
+ 0
+ 3
+ 4
+ 8
+ 6
+ 1
+ 0
+ 4
+ 5
+ 4
+ 3
+ 2
+ 6
+ 6
+ 4
+ 8
+ 2
+ 1
+ 3
+ 3
+ 9
+ 3
+ 6
+ 0
+ 7
+ 2
+ 6
+ 0
+ 2
+ 4
+ 9
+ 1
+ 4
+ 1
+ 2
+ 7
+ 3
+ 7
+ 2
+ 4
+ 5
+ 8
+ 7
+ 0
+ 0
+ 6
+ 6
+ 0
+ 6
+ 3
+ 1
+ 5
+ 5
+ 8
+ 8
+ 1
+ 7
+ 4
+ 8
+ 8
+ 1
+ 5
+ 2
+ 0
+ 9
+ 2
+ 0
+ 9
+ 6
+ 2
+ 8
+ 2
+ 9
+ 2
+ 5
+ 4
+ 0
+ 9
+ 1
+ 7
+ 1
+ 5
+ 3
+ 6
+ 4
+ 3
+ 6
+ 7
+ 8
+ 9
+ 2
+ 5
+ 9
+ 0
+ 3
+ 6
+ 0
+ 0
+ 1
+ 1
+ 3
+ 3
+ 0
+ 5
+ 3
+ 0
+ 5
+ 4
+ 8
+ 8
+ 2
+ 0
+ 4
+ 6
+ 6
+ 5
+ 2
+ 1
+ 3
+ 8
+ 4
+ 1
+ 4
+ 6
+ 9
+ 5
+ 1
+ 9
+ 4
+ 1
+ 5
+ 1
+ 1
+ 6
+ 0
+ 9
+ 4
+ 3
+ 3
+ 0
+ 5
+ 7
+ 2
+ 7
+ 0
+ 3
+ 6
+ 5
+ 7
+ 5
+ 9
+ 5
+ 9
+ 1
+ 9
+ 5
+ 3
+ 0
+ 9
+ 2
+ 1
+ 8
+ 6
+ 1
+ 1
+ 7
+ 3
+ 8
+ 1
+ 9
+ 3
+ 2
+ 6
+ 1
+ 1
+ 7
+ 9
+ 3
+ 1
+ 0
+ 5
+ 1
+ 1
+ 8
+ 5
+ 4
+ 8
+ 0
+ 7
+ 4
+ 4
+ 6
+ 2
+ 3
+ 7
+ 9
+ 9
+ 6
+ 2
+ 7
+ 4
+ 9
+ 5
+ 6
+ 7
+ 3
+ 5
+ 1
+ 8
+ 8
+ 5
+ 7
+ 5
+ 2
+ 7
+ 2
+ 4
+ 8
+ 9
+ 1
+ 2
+ 2
+ 7
+ 9
+ 3
+ 8
+ 1
+ 8
+ 3
+ 0
+ 1
+ 1
+ 9
+ 4
+ 9
+ 1
+ 2
+ 9
+ 8
+ 3
+ 3
+ 6
+ 7
+ 3
+ 3
+ 6
+ 2
+ 4
+ 4
+ 0
+ 6
+ 5
+ 6
+ 6
+ 4
+ 3
+ 0
+ 8
+ 6
+ 0
+ 2
+ 1
+ 3
+ 9
+ 4
+ 9
+ 4
+ 6
+ 3
+ 9
+ 5
+ 2
+ 2
+ 4
+ 7
+ 3
+ 7
+ 1
+ 9
+ 0
+ 7
+ 0
+ 2
+ 1
+ 7
+ 9
+ 8
+ 6
+ 0
+ 9
+ 4
+ 3
+ 7
+ 0
+ 2
+ 7
+ 7
+ 0
+ 5
+ 3
+ 9
+ 2
+ 1
+ 7
+ 1
+ 7
+ 6
+ 2
+ 9
+ 3
+ 1
+ 7
+ 6
+ 7
+ 5
+ 2
+ 3
+ 8
+ 4
+ 6
+ 7
+ 4
+ 8
+ 1
+ 8
+ 4
+ 6
+ 7
+ 6
+ 6
+ 9
+ 4
+ 0
+ 5
+ 1
+ 3
+ 2
+ 0
+ 0
+ 0
+ 5
+ 6
+ 8
+ 1
+ 2
+ 7
+ 1
+ 4
+ 5
+ 2
+ 6
+ 3
+ 5
+ 6
+ 0
+ 8
+ 2
+ 7
+ 7
+ 8
+ 5
+ 7
+ 7
+ 1
+ 3
+ 4
+ 2
+ 7
+ 5
+ 7
+ 7
+ 8
+ 9
+ 6
+ 0
+ 9
+ 1
+ 7
+ 3
+ 6
+ 3
+ 7
+ 1
+ 7
+ 8
+ 7
+ 2
+ 1
+ 4
+ 6
+ 8
+ 4
+ 4
+ 0
+ 9
+ 0
+ 1
+ 2
+ 2
+ 4
+ 9
+ 5
+ 3
+ 4
+ 3
+ 0
+ 1
+ 4
+ 6
+ 5
+ 4
+ 9
+ 5
+ 8
+ 5
+ 3
+ 7
+ 1
+ 0
+ 5
+ 0
+ 7
+ 9
+ 2
+ 2
+ 7
+ 9
+ 6
+ 8
+ 9
+ 2
+ 5
+ 8
+ 9
+ 2
+ 3
+ 5
+ 4
+ 2
+ 0
+ 1
+ 9
+ 9
+ 5
+ 6
+ 1
+ 1
+ 2
+ 1
+ 2
+ 9
+ 0
+ 2
+ 1
+ 9
+ 6
+ 0
+ 8
+ 6
+ 4
+ 0
+ 3
+ 4
+ 4
+ 1
+ 8
+ 1
+ 5
+ 9
+ 8
+ 1
+ 3
+ 6
+ 2
+ 9
+ 7
+ 7
+ 4
+ 7
+ 7
+ 1
+ 3
+ 0
+ 9
+ 9
+ 6
+ 0
+ 5
+ 1
+ 8
+ 7
+ 0
+ 7
+ 2
+ 1
+ 1
+ 3
+ 4
+ 9
+ 9
+ 9
+ 9
+ 9
+ 9
+ 8
+ 3
+ 7
+ 2
+ 9
+ 7
+ 8
+ 0
+ 4
+ 9
+ 9
+ 5
+ 1
+ 0
+ 5
+ 9
+ 7
+ 3
+ 1
+ 7
+ 3
+ 2
+ 8
+ 1
+ 6
+ 0
+ 9
+ 6
+ 3
+ 1
+ 8
+ 5
+ 9
+ 5
+ 0
+ 2
+ 4
+ 4
+ 5
+ 9
+ 4
+ 5
+ 5
+ 3
+ 4
+ 6
+ 9
+ 0
+ 8
+ 3
+ 0
+ 2
+ 6
+ 4
+ 2
+ 5
+ 2
+ 2
+ 3
+ 0
+ 8
+ 2
+ 5
+ 3
+ 3
+ 4
+ 4
+ 6
+ 8
+ 5
+ 0
+ 3
+ 5
+ 2
+ 6
+ 1
+ 9
+ 3
+ 1
+ 1
+ 8
+ 8
+ 1
+ 7
+ 1
+ 0
+ 1
+ 0
+ 0
+ 0
+ 3
+ 1
+ 3
+ 7
+ 8
+ 3
+ 8
+ 7
+ 5
+ 2
+ 8
+ 8
+ 6
+ 5
+ 8
+ 7
+ 5
+ 3
+ 3
+ 2
+ 0
+ 8
+ 3
+ 8
+ 1
+ 4
+ 2
+ 0
+ 6
+ 1
+ 7
+ 1
+ 7
+ 7
+ 6
+ 6
+ 9
+ 1
+ 4
+ 7
+ 3
+ 0
+ 3
+ 5
+ 9
+ 8
+ 2
+ 5
+ 3
+ 4
+ 9
+ 0
+ 4
+ 2
+ 8
+ 7
+ 5
+ 5
+ 4
+ 6
+ 8
+ 7
+ 3
+ 1
+ 1
+ 5
+ 9
+ 5
+ 6
+ 2
+ 8
+ 6
+ 3
+ 8
+ 8
+ 2
+ 3
+ 5
+ 3
+ 7
+ 8
+ 7
+ 5
+ 9
+ 3
+ 7
+ 5
+ 1
+ 9
+ 5
+ 7
+ 7
+ 8
+ 1
+ 8
+ 5
+ 7
+ 7
+ 3
+ 0
+ 5
+ 3
+ 2
+ 1
+ 7
+ 1
+ 2
+ 2
+ 6
+ 8
+ 0
+ 6
+ 6
+ 1
+ 3
+ 0
+ 0
+ 1
+ 9
+ 2
+ 7
+ 8
+ 7
+ 6
+ 6
+ 1
+ 1
+ 1
+ 9
+ 5
+ 9
+ 0
+ 9
+ 2
+ 1
+ 6
+ 4
+ 2
+ 0
+ 1
+ 9
+ 8
+ 9
+ 3
+ 8
+ 0
+ 9
+ 5
+ 2
+ 5
+ 7
+ 2
+ 0
+ 1
+ 0
+ 6
+ 5
+ 4
+ 8
+ 5
+ 8
+ 6
+ 3
+ 2
+ 7
+ 8
+ 8
+ 6
+ 5
+ 9
+ 3
+ 6
+ 1
+ 5
+ 3
+ 3
+ 8
+ 1
+ 8
+ 2
+ 7
+ 9
+ 6
+ 8
+ 2
+ 3
+ 0
+ 3
+ 0
+ 1
+ 9
+ 5
+ 2
+ 0
+ 3
+ 5
+ 3
+ 0
+ 1
+ 8
+ 5
+ 2
+ 9
+ 6
+ 8
+ 9
+ 9
+ 5
+ 7
+ 7
+ 3
+ 6
+ 2
+ 2
+ 5
+ 9
+ 9
+ 4
+ 1
+ 3
+ 8
+ 9
+ 1
+ 2
+ 4
+ 9
+ 7
+ 2
+ 1
+ 7
+ 7
+ 5
+ 2
+ 8
+ 3
+ 4
+ 7
+ 9
+ 1
+ 3
+ 1
+ 5
+ 1
+ 5
+ 5
+ 7
+ 4
+ 8
+ 5
+ 7
+ 2
+ 4
+ 2
+ 4
+ 5
+ 4
+ 1
+ 5
+ 0
+ 6
+ 9
+ 5
+ 9
+ 5
+ 0
+ 8
+ 2
+ 9
+ 5
+ 3
+ 3
+ 1
+ 1
+ 6
+ 8
+ 6
+ 1
+ 7
+ 2
+ 7
+ 8
+ 5
+ 5
+ 8
+ 8
+ 9
+ 0
+ 7
+ 5
+ 0
+ 9
+ 8
+ 3
+ 8
+ 1
+ 7
+ 5
+ 4
+ 6
+ 3
+ 7
+ 4
+ 6
+ 4
+ 9
+ 3
+ 9
+ 3
+ 1
+ 9
+ 2
+ 5
+ 5
+ 0
+ 6
+ 0
+ 4
+ 0
+ 0
+ 9
+ 2
+ 7
+ 7
+ 0
+ 1
+ 6
+ 7
+ 1
+ 1
+ 3
+ 9
+ 0
+ 0
+ 9
+ 8
+ 4
+ 8
+ 8
+ 2
+ 4
+ 0
+ 1
+ 2
+ 8
+ 5
+ 8
+ 3
+ 6
+ 1
+ 6
+ 0
+ 3
+ 5
+ 6
+ 3
+ 7
+ 0
+ 7
+ 6
+ 6
+ 0
+ 1
+ 0
+ 4
+ 7
+ 1
+ 0
+ 1
+ 8
+ 1
+ 9
+ 4
+ 2
+ 9
+ 5
+ 5
+ 5
+ 9
+ 6
+ 1
+ 9
+ 8
+ 9
+ 4
+ 6
+ 7
+ 6
+ 7
+ 8
+ 3
+ 7
+ 4
+ 4
+ 9
+ 4
+ 4
+ 8
+ 2
+ 5
+ 5
+ 3
+ 7
+ 9
+ 7
+ 7
+ 4
+ 7
+ 2
+ 6
+ 8
+ 4
+ 7
+ 1
+ 0
+ 4
+ 0
+ 4
+ 7
+ 5
+ 3
+ 4
+ 6
+ 4
+ 6
+ 2
+ 0
+ 8
+ 0
+ 4
+ 6
+ 6
+ 8
+ 4
+ 2
+ 5
+ 9
+ 0
+ 6
+ 9
+ 4
+ 9
+ 1
+ 2
+ 9
+ 3
+ 3
+ 1
+ 3
+ 6
+ 7
+ 7
+ 0
+ 2
+ 8
+ 9
+ 8
+ 9
+ 1
+ 5
+ 2
+ 1
+ 0
+ 4
+ 7
+ 5
+ 2
+ 1
+ 6
+ 2
+ 0
+ 5
+ 6
+ 9
+ 6
+ 6
+ 0
+ 2
+ 4
+ 0
+ 5
+ 8
+ 0
+ 3
+ 8
+ 1
+ 5
+ 0
+ 1
+ 9
+ 3
+ 5
+ 1
+ 1
+ 2
+ 5
+ 3
+ 3
+ 8
+ 2
+ 4
+ 3
+ 0
+ 0
+ 3
+ 5
+ 5
+ 8
+ 7
+ 6
+ 4
+ 0
+ 2
+ 4
+ 7
+ 4
+ 9
+ 6
+ 4
+ 7
+ 3
+ 2
+ 6
+ 3
+ 9
+ 1
+ 4
+ 1
+ 9
+ 9
+ 2
+ 7
+ 2
+ 6
+ 0
+ 4
+ 2
+ 6
+ 9
+ 9
+ 2
+ 2
+ 7
+ 9
+ 6
+ 7
+ 8
+ 2
+ 3
+ 5
+ 4
+ 7
+ 8
+ 1
+ 6
+ 3
+ 6
+ 0
+ 0
+ 9
+ 3
+ 4
+ 1
+ 7
+ 2
+ 1
+ 6
+ 4
+ 1
+ 2
+ 1
+ 9
+ 9
+ 2
+ 4
+ 5
+ 8
+ 6
+ 3
+ 1
+ 5
+ 0
+ 3
+ 0
+ 2
+ 8
+ 6
+ 1
+ 8
+ 2
+ 9
+ 7
+ 4
+ 5
+ 5
+ 5
+ 7
+ 0
+ 6
+ 7
+ 4
+ 9
+ 8
+ 3
+ 8
+ 5
+ 0
+ 5
+ 4
+ 9
+ 4
+ 5
+ 8
+ 8
+ 5
+ 8
+ 6
+ 9
+ 2
+ 6
+ 9
+ 9
+ 5
+ 6
+ 9
+ 0
+ 9
+ 2
+ 7
+ 2
+ 1
+ 0
+ 7
+ 9
+ 7
+ 5
+ 0
+ 9
+ 3
+ 0
+ 2
+ 9
+ 5
+ 5
+ 3
+ 2
+ 1
+ 1
+ 6
+ 5
+ 3
+ 4
+ 4
+ 9
+ 8
+ 7
+ 2
+ 0
+ 2
+ 7
+ 5
+ 5
+ 9
+ 6
+ 0
+ 2
+ 3
+ 6
+ 4
+ 8
+ 0
+ 6
+ 6
+ 5
+ 4
+ 9
+ 9
+ 1
+ 1
+ 9
+ 8
+ 8
+ 1
+ 8
+ 3
+ 4
+ 7
+ 9
+ 7
+ 7
+ 5
+ 3
+ 5
+ 6
+ 6
+ 3
+ 6
+ 9
+ 8
+ 0
+ 7
+ 4
+ 2
+ 6
+ 5
+ 4
+ 2
+ 5
+ 2
+ 7
+ 8
+ 6
+ 2
+ 5
+ 5
+ 1
+ 8
+ 1
+ 8
+ 4
+ 1
+ 7
+ 5
+ 7
+ 4
+ 6
+ 7
+ 2
+ 8
+ 9
+ 0
+ 9
+ 7
+ 7
+ 7
+ 7
+ 2
+ 7
+ 9
+ 3
+ 8
+ 0
+ 0
+ 0
+ 8
+ 1
+ 6
+ 4
+ 7
+ 0
+ 6
+ 0
+ 0
+ 1
+ 6
+ 1
+ 4
+ 5
+ 2
+ 4
+ 9
+ 1
+ 9
+ 2
+ 1
+ 7
+ 3
+ 2
+ 1
+ 7
+ 2
+ 1
+ 4
+ 7
+ 7
+ 2
+ 3
+ 5
+ 0
+ 1
+ 4
+ 1
+ 4
+ 4
+ 1
+ 9
+ 7
+ 3
+ 5
+ 6
+ 8
+ 5
+ 4
+ 8
+ 1
+ 6
+ 1
+ 3
+ 6
+ 1
+ 1
+ 5
+ 7
+ 3
+ 5
+ 2
+ 5
+ 5
+ 2
+ 1
+ 3
+ 3
+ 4
+ 7
+ 5
+ 7
+ 4
+ 1
+ 8
+ 4
+ 9
+ 4
+ 6
+ 8
+ 4
+ 3
+ 8
+ 5
+ 2
+ 3
+ 3
+ 2
+ 3
+ 9
+ 0
+ 7
+ 3
+ 9
+ 4
+ 1
+ 4
+ 3
+ 3
+ 3
+ 4
+ 5
+ 4
+ 7
+ 7
+ 6
+ 2
+ 4
+ 1
+ 6
+ 8
+ 6
+ 2
+ 5
+ 1
+ 8
+ 9
+ 8
+ 3
+ 5
+ 6
+ 9
+ 4
+ 8
+ 5
+ 5
+ 6
+ 2
+ 0
+ 9
+ 9
+ 2
+ 1
+ 9
+ 2
+ 2
+ 2
+ 1
+ 8
+ 4
+ 2
+ 7
+ 2
+ 5
+ 5
+ 0
+ 2
+ 5
+ 4
+ 2
+ 5
+ 6
+ 8
+ 8
+ 7
+ 6
+ 7
+ 1
+ 7
+ 9
+ 0
+ 4
+ 9
+ 4
+ 6
+ 0
+ 1
+ 6
+ 5
+ 3
+ 4
+ 6
+ 6
+ 8
+ 0
+ 4
+ 9
+ 8
+ 8
+ 6
+ 2
+ 7
+ 2
+ 3
+ 2
+ 7
+ 9
+ 1
+ 7
+ 8
+ 6
+ 0
+ 8
+ 5
+ 7
+ 8
+ 4
+ 3
+ 8
+ 3
+ 8
+ 2
+ 7
+ 9
+ 6
+ 7
+ 9
+ 7
+ 6
+ 6
+ 8
+ 1
+ 4
+ 5
+ 4
+ 1
+ 0
+ 0
+ 9
+ 5
+ 3
+ 8
+ 8
+ 3
+ 7
+ 8
+ 6
+ 3
+ 6
+ 0
+ 9
+ 5
+ 0
+ 6
+ 8
+ 0
+ 0
+ 6
+ 4
+ 2
+ 2
+ 5
+ 1
+ 2
+ 5
+ 2
+ 0
+ 5
+ 1
+ 1
+ 7
+ 3
+ 9
+ 2
+ 9
+ 8
+ 4
+ 8
+ 9
+ 6
+ 0
+ 8
+ 4
+ 1
+ 2
+ 8
+ 4
+ 8
+ 8
+ 6
+ 2
+ 6
+ 9
+ 4
+ 5
+ 6
+ 0
+ 4
+ 2
+ 4
+ 1
+ 9
+ 6
+ 5
+ 2
+ 8
+ 5
+ 0
+ 2
+ 2
+ 2
+ 1
+ 0
+ 6
+ 6
+ 1
+ 1
+ 8
+ 6
+ 3
+ 0
+ 6
+ 7
+ 4
+ 4
+ 2
+ 7
+ 8
+ 6
+ 2
+ 2
+ 0
+ 3
+ 9
+ 1
+ 9
+ 4
+ 9
+ 4
+ 5
+ 0
+ 4
+ 7
+ 1
+ 2
+ 3
+ 7
+ 1
+ 3
+ 7
+ 8
+ 6
+ 9
+ 6
+ 0
+ 9
+ 5
+ 6
+ 3
+ 6
+ 4
+ 3
+ 7
+ 1
+ 9
+ 1
+ 7
+ 2
+ 8
+ 7
+ 4
+ 6
+ 7
+ 7
+ 6
+ 4
+ 6
+ 5
+ 7
+ 5
+ 7
+ 3
+ 9
+ 6
+ 2
+ 4
+ 1
+ 3
+ 8
+ 9
+ 0
+ 8
+ 6
+ 5
+ 8
+ 3
+ 2
+ 6
+ 4
+ 5
+ 9
+ 9
+ 5
+ 8
+ 1
+ 3
+ 3
+ 9
+ 0
+ 4
+ 7
+ 8
+ 0
+ 2
+ 7
+ 5
+ 9
+ 0
+ 0
+ 9
+ 9
+ 4
+ 6
+ 5
+ 7
+ 6
+ 4
+ 0
+ 7
+ 8
+ 9
+ 5
+ 1
+ 2
+ 6
+ 9
+ 4
+ 6
+ 8
+ 3
+ 9
+ 8
+ 3
+ 5
+ 2
+ 5
+ 9
+ 5
+ 7
+ 0
+ 9
+ 8
+ 2
+ 5
+ 8
+ 2
+ 2
+ 6
+ 2
+ 0
+ 5
+ 2
+ 2
+ 4
+ 8
+ 9
+ 4
+ 0
+ 7
+ 7
+ 2
+ 6
+ 7
+ 1
+ 9
+ 4
+ 7
+ 8
+ 2
+ 6
+ 8
+ 4
+ 8
+ 2
+ 6
+ 0
+ 1
+ 4
+ 7
+ 6
+ 9
+ 9
+ 0
+ 9
+ 0
+ 2
+ 6
+ 4
+ 0
+ 1
+ 3
+ 6
+ 3
+ 9
+ 4
+ 4
+ 3
+ 7
+ 4
+ 5
+ 5
+ 3
+ 0
+ 5
+ 0
+ 6
+ 8
+ 2
+ 0
+ 3
+ 4
+ 9
+ 6
+ 2
+ 5
+ 2
+ 4
+ 5
+ 1
+ 7
+ 4
+ 9
+ 3
+ 9
+ 9
+ 6
+ 5
+ 1
+ 4
+ 3
+ 1
+ 4
+ 2
+ 9
+ 8
+ 0
+ 9
+ 1
+ 9
+ 0
+ 6
+ 5
+ 9
+ 2
+ 5
+ 0
+ 9
+ 3
+ 7
+ 2
+ 2
+ 1
+ 6
+ 9
+ 6
+ 4
+ 6
+ 1
+ 5
+ 1
+ 5
+ 7
+ 0
+ 9
+ 8
+ 5
+ 8
+ 3
+ 8
+ 7
+ 4
+ 1
+ 0
+ 5
+ 9
+ 7
+ 8
+ 8
+ 5
+ 9
+ 5
+ 9
+ 7
+ 7
+ 2
+ 9
+ 7
+ 5
+ 4
+ 9
+ 8
+ 9
+ 3
+ 0
+ 1
+ 6
+ 1
+ 7
+ 5
+ 3
+ 9
+ 2
+ 8
+ 4
+ 6
+ 8
+ 1
+ 3
+ 8
+ 2
+ 6
+ 8
+ 6
+ 8
+ 3
+ 8
+ 6
+ 8
+ 9
+ 4
+ 2
+ 7
+ 7
+ 4
+ 1
+ 5
+ 5
+ 9
+ 9
+ 1
+ 8
+ 5
+ 5
+ 9
+ 2
+ 5
+ 2
+ 4
+ 5
+ 9
+ 5
+ 3
+ 9
+ 5
+ 9
+ 4
+ 3
+ 1
+ 0
+ 4
+ 9
+ 9
+ 7
+ 2
+ 5
+ 2
+ 4
+ 6
+ 8
+ 0
+ 8
+ 4
+ 5
+ 9
+ 8
+ 7
+ 2
+ 7
+ 3
+ 6
+ 4
+ 4
+ 6
+ 9
+ 5
+ 8
+ 4
+ 8
+ 6
+ 5
+ 3
+ 8
+ 3
+ 6
+ 7
+ 3
+ 6
+ 2
+ 2
+ 2
+ 6
+ 2
+ 6
+ 0
+ 9
+ 9
+ 1
+ 2
+ 4
+ 6
+ 0
+ 8
+ 0
+ 5
+ 1
+ 2
+ 4
+ 3
+ 8
+ 8
+ 4
+ 3
+ 9
+ 0
+ 4
+ 5
+ 1
+ 2
+ 4
+ 4
+ 1
+ 3
+ 6
+ 5
+ 4
+ 9
+ 7
+ 6
+ 2
+ 7
+ 8
+ 0
+ 7
+ 9
+ 7
+ 7
+ 1
+ 5
+ 6
+ 9
+ 1
+ 4
+ 3
+ 5
+ 9
+ 9
+ 7
+ 7
+ 0
+ 0
+ 1
+ 2
+ 9
+ 6
+ 1
+ 6
+ 0
+ 8
+ 9
+ 4
+ 4
+ 1
+ 6
+ 9
+ 4
+ 8
+ 6
+ 8
+ 5
+ 5
+ 5
+ 8
+ 4
+ 8
+ 4
+ 0
+ 6
+ 3
+ 5
+ 3
+ 4
+ 2
+ 2
+ 0
+ 7
+ 2
+ 2
+ 2
+ 5
+ 8
+ 2
+ 8
+ 4
+ 8
+ 8
+ 6
+ 4
+ 8
+ 1
+ 5
+ 8
+ 4
+ 5
+ 6
+ 0
+ 2
+ 8
+ 5
+ 0
+ 6
+ 0
+ 1
+ 6
+ 8
+ 4
+ 2
+ 7
+ 3
+ 9
+ 4
+ 5
+ 2
+ 2
+ 6
+ 7
+ 4
+ 6
+ 7
+ 6
+ 7
+ 8
+ 8
+ 9
+ 5
+ 2
+ 5
+ 2
+ 1
+ 3
+ 8
+ 5
+ 2
+ 2
+ 5
+ 4
+ 9
+ 9
+ 5
+ 4
+ 6
+ 6
+ 6
+ 7
+ 2
+ 7
+ 8
+ 2
+ 3
+ 9
+ 8
+ 6
+ 4
+ 5
+ 6
+ 5
+ 9
+ 6
+ 1
+ 1
+ 6
+ 3
+ 5
+ 4
+ 8
+ 8
+ 6
+ 2
+ 3
+ 0
+ 5
+ 7
+ 7
+ 4
+ 5
+ 6
+ 4
+ 9
+ 8
+ 0
+ 3
+ 5
+ 5
+ 9
+ 3
+ 6
+ 3
+ 4
+ 5
+ 6
+ 8
+ 1
+ 7
+ 4
+ 3
+ 2
+ 4
+ 1
+ 1
+ 2
+ 5
+ 1
+ 5
+ 0
+ 7
+ 6
+ 0
+ 6
+ 9
+ 4
+ 7
+ 9
+ 4
+ 5
+ 1
+ 0
+ 9
+ 6
+ 5
+ 9
+ 6
+ 0
+ 9
+ 4
+ 0
+ 2
+ 5
+ 2
+ 2
+ 8
+ 8
+ 7
+ 9
+ 7
+ 1
+ 0
+ 8
+ 9
+ 3
+ 1
+ 4
+ 5
+ 6
+ 6
+ 9
+ 1
+ 3
+ 6
+ 8
+ 6
+ 7
+ 2
+ 2
+ 8
+ 7
+ 4
+ 8
+ 9
+ 4
+ 0
+ 5
+ 6
+ 0
+ 1
+ 0
+ 1
+ 5
+ 0
+ 3
+ 3
+ 0
+ 8
+ 6
+ 1
+ 7
+ 9
+ 2
+ 8
+ 6
+ 8
+ 0
+ 9
+ 2
+ 0
+ 8
+ 7
+ 4
+ 7
+ 6
+ 0
+ 9
+ 1
+ 7
+ 8
+ 2
+ 4
+ 9
+ 3
+ 8
+ 5
+ 8
+ 9
+ 0
+ 0
+ 9
+ 7
+ 1
+ 4
+ 9
+ 0
+ 9
+ 6
+ 7
+ 5
+ 9
+ 8
+ 5
+ 2
+ 6
+ 1
+ 3
+ 6
+ 5
+ 5
+ 4
+ 9
+ 7
+ 8
+ 1
+ 8
+ 9
+ 3
+ 1
+ 2
+ 9
+ 7
+ 8
+ 4
+ 8
+ 2
+ 1
+ 6
+ 8
+ 2
+ 9
+ 9
+ 8
+ 9
+ 4
+ 8
+ 7
+ 2
+ 2
+ 6
+ 5
+ 8
+ 8
+ 0
+ 4
+ 8
+ 5
+ 7
+ 5
+ 6
+ 4
+ 0
+ 1
+ 4
+ 2
+ 7
+ 0
+ 4
+ 7
+ 7
+ 5
+ 5
+ 5
+ 1
+ 3
+ 2
+ 3
+ 7
+ 9
+ 6
+ 4
+ 1
+ 4
+ 5
+ 1
+ 5
+ 2
+ 3
+ 7
+ 4
+ 6
+ 2
+ 3
+ 4
+ 3
+ 6
+ 4
+ 5
+ 4
+ 2
+ 8
+ 5
+ 8
+ 4
+ 4
+ 4
+ 7
+ 9
+ 5
+ 2
+ 6
+ 5
+ 8
+ 6
+ 7
+ 8
+ 2
+ 1
+ 0
+ 5
+ 1
+ 1
+ 4
+ 1
+ 3
+ 5
+ 4
+ 7
+ 3
+ 5
+ 7
+ 3
+ 9
+ 5
+ 2
+ 3
+ 1
+ 1
+ 3
+ 4
+ 2
+ 7
+ 1
+ 6
+ 6
+ 1
+ 0
+ 2
+ 1
+ 3
+ 5
+ 9
+ 6
+ 9
+ 5
+ 3
+ 6
+ 2
+ 3
+ 1
+ 4
+ 4
+ 2
+ 9
+ 5
+ 2
+ 4
+ 8
+ 4
+ 9
+ 3
+ 7
+ 1
+ 8
+ 7
+ 1
+ 1
+ 0
+ 1
+ 4
+ 5
+ 7
+ 6
+ 5
+ 4
+ 0
+ 3
+ 5
+ 9
+ 0
+ 2
+ 7
+ 9
+ 9
+ 3
+ 4
+ 4
+ 0
+ 3
+ 7
+ 4
+ 2
+ 0
+ 0
+ 7
+ 3
+ 1
+ 0
+ 5
+ 7
+ 8
+ 5
+ 3
+ 9
+ 0
+ 6
+ 2
+ 1
+ 9
+ 8
+ 3
+ 8
+ 7
+ 4
+ 4
+ 7
+ 8
+ 0
+ 8
+ 4
+ 7
+ 8
+ 4
+ 8
+ 9
+ 6
+ 8
+ 3
+ 3
+ 2
+ 1
+ 4
+ 4
+ 5
+ 7
+ 1
+ 3
+ 8
+ 6
+ 8
+ 7
+ 5
+ 1
+ 9
+ 4
+ 3
+ 5
+ 0
+ 6
+ 4
+ 3
+ 0
+ 2
+ 1
+ 8
+ 4
+ 5
+ 3
+ 1
+ 9
+ 1
+ 0
+ 4
+ 8
+ 4
+ 8
+ 1
+ 0
+ 0
+ 5
+ 3
+ 7
+ 0
+ 6
+ 1
+ 4
+ 6
+ 8
+ 0
+ 6
+ 7
+ 4
+ 9
+ 1
+ 9
+ 2
+ 7
+ 8
+ 1
+ 9
+ 1
+ 1
+ 9
+ 7
+ 9
+ 3
+ 9
+ 9
+ 5
+ 2
+ 0
+ 6
+ 1
+ 4
+ 1
+ 9
+ 6
+ 6
+ 3
+ 4
+ 2
+ 8
+ 7
+ 5
+ 4
+ 4
+ 4
+ 0
+ 6
+ 4
+ 3
+ 7
+ 4
+ 5
+ 1
+ 2
+ 3
+ 7
+ 1
+ 8
+ 1
+ 9
+ 2
+ 1
+ 7
+ 9
+ 9
+ 9
+ 8
+ 3
+ 9
+ 1
+ 0
+ 1
+ 5
+ 9
+ 1
+ 9
+ 5
+ 6
+ 1
+ 8
+ 1
+ 4
+ 6
+ 7
+ 5
+ 1
+ 4
+ 2
+ 6
+ 9
+ 1
+ 2
+ 3
+ 9
+ 7
+ 4
+ 8
+ 9
+ 4
+ 0
+ 9
+ 0
+ 7
+ 1
+ 8
+ 6
+ 4
+ 9
+ 4
+ 2
+ 3
+ 1
+ 9
+ 6
+ 1
+ 5
+ 6
+ 7
+ 9
+ 4
+ 5
+ 2
+ 0
+ 8
+ 0
+ 9
+ 5
+ 1
+ 4
+ 6
+ 5
+ 5
+ 0
+ 2
+ 2
+ 5
+ 2
+ 3
+ 1
+ 6
+ 0
+ 3
+ 8
+ 8
+ 1
+ 9
+ 3
+ 0
+ 1
+ 4
+ 2
+ 0
+ 9
+ 3
+ 7
+ 6
+ 2
+ 1
+ 3
+ 7
+ 8
+ 5
+ 5
+ 9
+ 5
+ 6
+ 6
+ 3
+ 8
+ 9
+ 3
+ 7
+ 7
+ 8
+ 7
+ 0
+ 8
+ 3
+ 0
+ 3
+ 9
+ 0
+ 6
+ 9
+ 7
+ 9
+ 2
+ 0
+ 7
+ 7
+ 3
+ 4
+ 6
+ 7
+ 2
+ 2
+ 1
+ 8
+ 2
+ 5
+ 6
+ 2
+ 5
+ 9
+ 9
+ 6
+ 6
+ 1
+ 5
+ 0
+ 1
+ 4
+ 2
+ 1
+ 5
+ 0
+ 3
+ 0
+ 6
+ 8
+ 0
+ 3
+ 8
+ 4
+ 4
+ 7
+ 7
+ 3
+ 4
+ 5
+ 4
+ 9
+ 2
+ 0
+ 2
+ 6
+ 0
+ 5
+ 4
+ 1
+ 4
+ 6
+ 6
+ 5
+ 9
+ 2
+ 5
+ 2
+ 0
+ 1
+ 4
+ 9
+ 7
+ 4
+ 4
+ 2
+ 8
+ 5
+ 0
+ 7
+ 3
+ 2
+ 5
+ 1
+ 8
+ 6
+ 6
+ 6
+ 0
+ 0
+ 2
+ 1
+ 3
+ 2
+ 4
+ 3
+ 4
+ 0
+ 8
+ 8
+ 1
+ 9
+ 0
+ 7
+ 1
+ 0
+ 4
+ 8
+ 6
+ 3
+ 3
+ 1
+ 7
+ 3
+ 4
+ 6
+ 4
+ 9
+ 6
+ 5
+ 1
+ 4
+ 5
+ 3
+ 9
+ 0
+ 5
+ 7
+ 9
+ 6
+ 2
+ 6
+ 8
+ 5
+ 6
+ 1
+ 0
+ 0
+ 5
+ 5
+ 0
+ 8
+ 1
+ 0
+ 6
+ 6
+ 5
+ 8
+ 7
+ 9
+ 6
+ 9
+ 9
+ 8
+ 1
+ 6
+ 3
+ 5
+ 7
+ 4
+ 7
+ 3
+ 6
+ 3
+ 8
+ 4
+ 0
+ 5
+ 2
+ 5
+ 7
+ 1
+ 4
+ 5
+ 9
+ 1
+ 0
+ 2
+ 8
+ 9
+ 7
+ 0
+ 6
+ 4
+ 1
+ 4
+ 0
+ 1
+ 1
+ 0
+ 9
+ 7
+ 1
+ 2
+ 0
+ 6
+ 2
+ 8
+ 0
+ 4
+ 3
+ 9
+ 0
+ 3
+ 9
+ 7
+ 5
+ 9
+ 5
+ 1
+ 5
+ 6
+ 7
+ 7
+ 1
+ 5
+ 7
+ 7
+ 0
+ 0
+ 4
+ 2
+ 0
+ 3
+ 3
+ 7
+ 8
+ 6
+ 9
+ 9
+ 3
+ 6
+ 0
+ 0
+ 7
+ 2
+ 3
+ 0
+ 5
+ 5
+ 8
+ 7
+ 6
+ 3
+ 1
+ 7
+ 6
+ 3
+ 5
+ 9
+ 4
+ 2
+ 1
+ 8
+ 7
+ 3
+ 1
+ 2
+ 5
+ 1
+ 4
+ 7
+ 1
+ 2
+ 0
+ 5
+ 3
+ 2
+ 9
+ 2
+ 8
+ 1
+ 9
+ 1
+ 8
+ 2
+ 6
+ 1
+ 8
+ 6
+ 1
+ 2
+ 5
+ 8
+ 6
+ 7
+ 3
+ 2
+ 1
+ 5
+ 7
+ 9
+ 1
+ 9
+ 8
+ 4
+ 1
+ 4
+ 8
+ 4
+ 8
+ 8
+ 2
+ 9
+ 1
+ 6
+ 4
+ 4
+ 7
+ 0
+ 6
+ 0
+ 9
+ 5
+ 7
+ 5
+ 2
+ 7
+ 0
+ 6
+ 9
+ 5
+ 7
+ 2
+ 2
+ 0
+ 9
+ 1
+ 7
+ 5
+ 6
+ 7
+ 1
+ 1
+ 6
+ 7
+ 2
+ 2
+ 9
+ 1
+ 0
+ 9
+ 8
+ 1
+ 6
+ 9
+ 0
+ 9
+ 1
+ 5
+ 2
+ 8
+ 0
+ 1
+ 7
+ 3
+ 5
+ 0
+ 6
+ 7
+ 1
+ 2
+ 7
+ 4
+ 8
+ 5
+ 8
+ 3
+ 2
+ 2
+ 2
+ 8
+ 7
+ 1
+ 8
+ 3
+ 5
+ 2
+ 0
+ 9
+ 3
+ 5
+ 3
+ 9
+ 6
+ 5
+ 7
+ 2
+ 5
+ 1
+ 2
+ 1
+ 0
+ 8
+ 3
+ 5
+ 7
+ 9
+ 1
+ 5
+ 1
+ 3
+ 6
+ 9
+ 8
+ 8
+ 2
+ 0
+ 9
+ 1
+ 4
+ 4
+ 4
+ 2
+ 1
+ 0
+ 0
+ 6
+ 7
+ 5
+ 1
+ 0
+ 3
+ 3
+ 4
+ 6
+ 7
+ 1
+ 1
+ 0
+ 3
+ 1
+ 4
+ 1
+ 2
+ 6
+ 7
+ 1
+ 1
+ 1
+ 3
+ 6
+ 9
+ 9
+ 0
+ 8
+ 6
+ 5
+ 8
+ 5
+ 1
+ 6
+ 3
+ 9
+ 8
+ 3
+ 1
+ 5
+ 0
+ 1
+ 9
+ 7
+ 0
+ 1
+ 6
+ 5
+ 1
+ 5
+ 1
+ 1
+ 6
+ 8
+ 5
+ 1
+ 7
+ 1
+ 4
+ 3
+ 7
+ 6
+ 5
+ 7
+ 6
+ 1
+ 8
+ 3
+ 5
+ 1
+ 5
+ 5
+ 6
+ 5
+ 0
+ 8
+ 8
+ 4
+ 9
+ 0
+ 9
+ 9
+ 8
+ 9
+ 8
+ 5
+ 9
+ 9
+ 8
+ 2
+ 3
+ 8
+ 7
+ 3
+ 4
+ 5
+ 5
+ 2
+ 8
+ 3
+ 3
+ 1
+ 6
+ 3
+ 5
+ 5
+ 0
+ 7
+ 6
+ 4
+ 7
+ 9
+ 1
+ 8
+ 5
+ 3
+ 5
+ 8
+ 9
+ 3
+ 2
+ 2
+ 6
+ 1
+ 8
+ 5
+ 4
+ 8
+ 9
+ 6
+ 3
+ 2
+ 1
+ 3
+ 2
+ 9
+ 3
+ 3
+ 0
+ 8
+ 9
+ 8
+ 5
+ 7
+ 0
+ 6
+ 4
+ 2
+ 0
+ 4
+ 6
+ 7
+ 5
+ 2
+ 5
+ 9
+ 0
+ 7
+ 0
+ 9
+ 1
+ 5
+ 4
+ 8
+ 1
+ 4
+ 1
+ 6
+ 5
+ 4
+ 9
+ 8
+ 5
+ 9
+ 4
+ 6
+ 1
+ 6
+ 3
+ 7
+ 1
+ 8
+ 0
+ 2
+ 7
+ 0
+ 9
+ 8
+ 1
+ 9
+ 9
+ 4
+ 3
+ 0
+ 9
+ 9
+ 2
+ 4
+ 4
+ 8
+ 8
+ 9
+ 5
+ 7
+ 5
+ 7
+ 1
+ 2
+ 8
+ 2
+ 8
+ 9
+ 0
+ 5
+ 9
+ 2
+ 3
+ 2
+ 3
+ 3
+ 2
+ 6
+ 0
+ 9
+ 7
+ 2
+ 9
+ 9
+ 7
+ 1
+ 2
+ 0
+ 8
+ 4
+ 4
+ 3
+ 3
+ 5
+ 7
+ 3
+ 2
+ 6
+ 5
+ 4
+ 8
+ 9
+ 3
+ 8
+ 2
+ 3
+ 9
+ 1
+ 1
+ 9
+ 3
+ 2
+ 5
+ 9
+ 7
+ 4
+ 6
+ 3
+ 6
+ 6
+ 7
+ 3
+ 0
+ 5
+ 8
+ 3
+ 6
+ 0
+ 4
+ 1
+ 4
+ 2
+ 8
+ 1
+ 3
+ 8
+ 8
+ 3
+ 0
+ 3
+ 2
+ 0
+ 3
+ 8
+ 2
+ 4
+ 9
+ 0
+ 3
+ 7
+ 5
+ 8
+ 9
+ 8
+ 5
+ 2
+ 4
+ 3
+ 7
+ 4
+ 4
+ 1
+ 7
+ 0
+ 2
+ 9
+ 1
+ 3
+ 2
+ 7
+ 6
+ 5
+ 6
+ 1
+ 8
+ 0
+ 9
+ 3
+ 7
+ 7
+ 3
+ 4
+ 4
+ 4
+ 0
+ 3
+ 0
+ 7
+ 0
+ 7
+ 4
+ 6
+ 9
+ 2
+ 1
+ 1
+ 2
+ 0
+ 1
+ 9
+ 1
+ 3
+ 0
+ 2
+ 0
+ 3
+ 3
+ 0
+ 3
+ 8
+ 0
+ 1
+ 9
+ 7
+ 6
+ 2
+ 1
+ 1
+ 0
+ 1
+ 1
+ 0
+ 0
+ 4
+ 4
+ 9
+ 2
+ 9
+ 3
+ 2
+ 1
+ 5
+ 1
+ 6
+ 0
+ 8
+ 4
+ 2
+ 4
+ 4
+ 4
+ 8
+ 5
+ 9
+ 6
+ 3
+ 7
+ 6
+ 6
+ 9
+ 8
+ 3
+ 8
+ 9
+ 5
+ 2
+ 2
+ 8
+ 6
+ 8
+ 4
+ 7
+ 8
+ 3
+ 1
+ 2
+ 3
+ 5
+ 5
+ 2
+ 6
+ 5
+ 8
+ 2
+ 1
+ 3
+ 1
+ 4
+ 4
+ 9
+ 5
+ 7
+ 6
+ 8
+ 5
+ 7
+ 2
+ 6
+ 2
+ 4
+ 3
+ 3
+ 4
+ 4
+ 1
+ 8
+ 9
+ 3
+ 0
+ 3
+ 9
+ 6
+ 8
+ 6
+ 4
+ 2
+ 6
+ 2
+ 4
+ 3
+ 4
+ 1
+ 0
+ 7
+ 7
+ 3
+ 2
+ 2
+ 6
+ 9
+ 7
+ 8
+ 0
+ 2
+ 8
+ 0
+ 7
+ 3
+ 1
+ 8
+ 9
+ 1
+ 5
+ 4
+ 4
+ 1
+ 1
+ 0
+ 1
+ 0
+ 4
+ 4
+ 6
+ 8
+ 2
+ 3
+ 2
+ 5
+ 2
+ 7
+ 1
+ 6
+ 2
+ 0
+ 1
+ 0
+ 5
+ 2
+ 6
+ 5
+ 2
+ 2
+ 7
+ 2
+ 1
+ 1
+ 1
+ 6
+ 6
+ 0
+ 3
+ 9
+ 6
+ 6
+ 6
+ 5
+ 5
+ 7
+ 3
+ 0
+ 9
+ 2
+ 5
+ 4
+ 7
+ 1
+ 1
+ 0
+ 5
+ 5
+ 7
+ 8
+ 5
+ 3
+ 7
+ 6
+ 3
+ 4
+ 6
+ 6
+ 8
+ 2
+ 0
+ 6
+ 5
+ 3
+ 1
+ 0
+ 9
+ 8
+ 9
+ 6
+ 5
+ 2
+ 6
+ 9
+ 1
+ 8
+ 6
+ 2
+ 0
+ 5
+ 6
+ 4
+ 7
+ 6
+ 9
+ 3
+ 1
+ 2
+ 5
+ 7
+ 0
+ 5
+ 8
+ 6
+ 3
+ 5
+ 6
+ 6
+ 2
+ 0
+ 1
+ 8
+ 5
+ 5
+ 8
+ 1
+ 0
+ 0
+ 7
+ 2
+ 9
+ 3
+ 6
+ 0
+ 6
+ 5
+ 9
+ 8
+ 7
+ 6
+ 4
+ 8
+ 6
+ 1
+ 1
+ 7
+ 9
+ 1
+ 0
+ 4
+ 5
+ 3
+ 3
+ 4
+ 8
+ 8
+ 5
+ 0
+ 3
+ 4
+ 6
+ 1
+ 1
+ 3
+ 6
+ 5
+ 7
+ 6
+ 8
+ 6
+ 7
+ 5
+ 3
+ 2
+ 4
+ 9
+ 4
+ 4
+ 1
+ 6
+ 6
+ 8
+ 0
+ 3
+ 9
+ 6
+ 2
+ 6
+ 5
+ 7
+ 9
+ 7
+ 8
+ 7
+ 7
+ 1
+ 8
+ 5
+ 5
+ 6
+ 0
+ 8
+ 4
+ 5
+ 5
+ 2
+ 9
+ 6
+ 5
+ 4
+ 1
+ 2
+ 6
+ 6
+ 5
+ 4
+ 0
+ 8
+ 5
+ 3
+ 0
+ 6
+ 1
+ 4
+ 3
+ 4
+ 4
+ 4
+ 3
+ 1
+ 8
+ 5
+ 8
+ 6
+ 7
+ 6
+ 9
+ 7
+ 5
+ 1
+ 4
+ 5
+ 6
+ 6
+ 1
+ 4
+ 0
+ 6
+ 8
+ 0
+ 0
+ 7
+ 0
+ 0
+ 2
+ 3
+ 7
+ 8
+ 7
+ 7
+ 6
+ 5
+ 9
+ 1
+ 3
+ 4
+ 4
+ 0
+ 1
+ 7
+ 1
+ 2
+ 7
+ 4
+ 9
+ 4
+ 7
+ 0
+ 4
+ 2
+ 0
+ 5
+ 6
+ 2
+ 2
+ 3
+ 0
+ 5
+ 3
+ 8
+ 9
+ 9
+ 4
+ 5
+ 6
+ 1
+ 3
+ 1
+ 4
+ 0
+ 7
+ 1
+ 1
+ 2
+ 7
+ 0
+ 0
+ 0
+ 4
+ 0
+ 7
+ 8
+ 5
+ 4
+ 7
+ 3
+ 3
+ 2
+ 6
+ 9
+ 9
+ 3
+ 9
+ 0
+ 8
+ 1
+ 4
+ 5
+ 4
+ 6
+ 6
+ 4
+ 6
+ 4
+ 5
+ 8
+ 8
+ 0
+ 7
+ 9
+ 7
+ 2
+ 7
+ 0
+ 8
+ 2
+ 6
+ 6
+ 8
+ 3
+ 0
+ 6
+ 3
+ 4
+ 3
+ 2
+ 8
+ 5
+ 8
+ 7
+ 8
+ 5
+ 6
+ 9
+ 8
+ 3
+ 0
+ 5
+ 2
+ 3
+ 5
+ 8
+ 0
+ 8
+ 9
+ 3
+ 3
+ 0
+ 6
+ 5
+ 7
+ 5
+ 7
+ 4
+ 0
+ 6
+ 7
+ 9
+ 5
+ 4
+ 5
+ 7
+ 1
+ 6
+ 3
+ 7
+ 7
+ 5
+ 2
+ 5
+ 4
+ 2
+ 0
+ 2
+ 1
+ 1
+ 4
+ 9
+ 5
+ 5
+ 7
+ 6
+ 1
+ 5
+ 8
+ 1
+ 4
+ 0
+ 0
+ 2
+ 5
+ 0
+ 1
+ 2
+ 6
+ 2
+ 2
+ 8
+ 5
+ 9
+ 4
+ 1
+ 3
+ 0
+ 2
+ 1
+ 6
+ 4
+ 7
+ 1
+ 5
+ 5
+ 0
+ 9
+ 7
+ 9
+ 2
+ 5
+ 9
+ 2
+ 3
+ 0
+ 9
+ 9
+ 0
+ 7
+ 9
+ 6
+ 5
+ 4
+ 7
+ 3
+ 7
+ 6
+ 1
+ 2
+ 5
+ 5
+ 1
+ 7
+ 6
+ 5
+ 6
+ 7
+ 5
+ 1
+ 3
+ 5
+ 7
+ 5
+ 1
+ 7
+ 8
+ 2
+ 9
+ 6
+ 6
+ 6
+ 4
+ 5
+ 4
+ 7
+ 7
+ 9
+ 1
+ 7
+ 4
+ 5
+ 0
+ 1
+ 1
+ 2
+ 9
+ 9
+ 6
+ 1
+ 4
+ 8
+ 9
+ 0
+ 3
+ 0
+ 4
+ 6
+ 3
+ 9
+ 9
+ 4
+ 7
+ 1
+ 3
+ 2
+ 9
+ 6
+ 2
+ 1
+ 0
+ 7
+ 3
+ 4
+ 0
+ 4
+ 3
+ 7
+ 5
+ 1
+ 8
+ 9
+ 5
+ 7
+ 3
+ 5
+ 9
+ 6
+ 1
+ 4
+ 5
+ 8
+ 9
+ 0
+ 1
+ 9
+ 3
+ 8
+ 9
+ 7
+ 1
+ 3
+ 1
+ 1
+ 1
+ 7
+ 9
+ 0
+ 4
+ 2
+ 9
+ 7
+ 8
+ 2
+ 8
+ 5
+ 6
+ 4
+ 7
+ 5
+ 0
+ 3
+ 2
+ 0
+ 3
+ 1
+ 9
+ 8
+ 6
+ 9
+ 1
+ 5
+ 1
+ 4
+ 0
+ 2
+ 8
+ 7
+ 0
+ 8
+ 0
+ 8
+ 5
+ 9
+ 9
+ 0
+ 4
+ 8
+ 0
+ 1
+ 0
+ 9
+ 4
+ 1
+ 2
+ 1
+ 4
+ 7
+ 2
+ 2
+ 1
+ 3
+ 1
+ 7
+ 9
+ 4
+ 7
+ 6
+ 4
+ 7
+ 7
+ 7
+ 2
+ 6
+ 2
+ 2
+ 4
+ 1
+ 4
+ 2
+ 5
+ 4
+ 8
+ 5
+ 4
+ 5
+ 4
+ 0
+ 3
+ 3
+ 2
+ 1
+ 5
+ 7
+ 1
+ 8
+ 5
+ 3
+ 0
+ 6
+ 1
+ 4
+ 2
+ 2
+ 8
+ 8
+ 1
+ 3
+ 7
+ 5
+ 8
+ 5
+ 0
+ 4
+ 3
+ 0
+ 6
+ 3
+ 3
+ 2
+ 1
+ 7
+ 5
+ 1
+ 8
+ 2
+ 9
+ 7
+ 9
+ 8
+ 6
+ 6
+ 2
+ 2
+ 3
+ 7
+ 1
+ 7
+ 2
+ 1
+ 5
+ 9
+ 1
+ 6
+ 0
+ 7
+ 7
+ 1
+ 6
+ 6
+ 9
+ 2
+ 5
+ 4
+ 7
+ 4
+ 8
+ 7
+ 3
+ 8
+ 9
+ 8
+ 6
+ 6
+ 5
+ 4
+ 9
+ 4
+ 9
+ 4
+ 5
+ 0
+ 1
+ 1
+ 4
+ 6
+ 5
+ 4
+ 0
+ 6
+ 2
+ 8
+ 4
+ 3
+ 3
+ 6
+ 6
+ 3
+ 9
+ 3
+ 7
+ 9
+ 0
+ 0
+ 3
+ 9
+ 7
+ 6
+ 9
+ 2
+ 6
+ 5
+ 6
+ 7
+ 2
+ 1
+ 4
+ 6
+ 3
+ 8
+ 5
+ 3
+ 0
+ 6
+ 7
+ 3
+ 6
+ 0
+ 9
+ 6
+ 5
+ 7
+ 1
+ 2
+ 0
+ 9
+ 1
+ 8
+ 0
+ 7
+ 6
+ 3
+ 8
+ 3
+ 2
+ 7
+ 1
+ 6
+ 6
+ 4
+ 1
+ 6
+ 2
+ 7
+ 4
+ 8
+ 8
+ 8
+ 8
+ 0
+ 0
+ 7
+ 8
+ 6
+ 9
+ 2
+ 5
+ 6
+ 0
+ 2
+ 9
+ 0
+ 2
+ 2
+ 8
+ 4
+ 7
+ 2
+ 1
+ 0
+ 4
+ 0
+ 3
+ 1
+ 7
+ 2
+ 1
+ 1
+ 8
+ 6
+ 0
+ 8
+ 2
+ 0
+ 4
+ 1
+ 9
+ 0
+ 0
+ 0
+ 4
+ 2
+ 2
+ 9
+ 6
+ 6
+ 1
+ 7
+ 1
+ 1
+ 9
+ 6
+ 3
+ 7
+ 7
+ 9
+ 2
+ 1
+ 3
+ 3
+ 7
+ 5
+ 7
+ 5
+ 1
+ 1
+ 4
+ 9
+ 5
+ 9
+ 5
+ 0
+ 1
+ 5
+ 6
+ 6
+ 0
+ 4
+ 9
+ 6
+ 3
+ 1
+ 8
+ 6
+ 2
+ 9
+ 4
+ 7
+ 2
+ 6
+ 5
+ 4
+ 7
+ 3
+ 6
+ 4
+ 2
+ 5
+ 2
+ 3
+ 0
+ 8
+ 1
+ 7
+ 7
+ 0
+ 3
+ 6
+ 7
+ 5
+ 1
+ 5
+ 9
+ 0
+ 6
+ 7
+ 3
+ 5
+ 0
+ 2
+ 3
+ 5
+ 0
+ 7
+ 2
+ 8
+ 3
+ 5
+ 4
+ 0
+ 5
+ 6
+ 7
+ 0
+ 4
+ 0
+ 3
+ 8
+ 6
+ 7
+ 4
+ 3
+ 5
+ 1
+ 3
+ 6
+ 2
+ 2
+ 2
+ 2
+ 4
+ 7
+ 7
+ 1
+ 5
+ 8
+ 9
+ 1
+ 5
+ 0
+ 4
+ 9
+ 5
+ 3
+ 0
+ 9
+ 8
+ 4
+ 4
+ 4
+ 8
+ 9
+ 3
+ 3
+ 3
+ 0
+ 9
+ 6
+ 3
+ 4
+ 0
+ 8
+ 7
+ 8
+ 0
+ 7
+ 6
+ 9
+ 3
+ 2
+ 5
+ 9
+ 9
+ 3
+ 9
+ 7
+ 8
+ 0
+ 5
+ 4
+ 1
+ 9
+ 3
+ 4
+ 1
+ 4
+ 4
+ 7
+ 3
+ 7
+ 7
+ 4
+ 4
+ 1
+ 8
+ 4
+ 2
+ 6
+ 3
+ 1
+ 2
+ 9
+ 8
+ 6
+ 0
+ 8
+ 0
+ 9
+ 9
+ 8
+ 8
+ 8
+ 6
+ 8
+ 7
+ 4
+ 1
+ 3
+ 2
+ 6
+ 0
+ 4
+ 7
+ 2
diff --git a/test-jar.xml b/test-jar.xml
new file mode 100644
index 0000000..81316ee
--- /dev/null
+++ b/test-jar.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Compiles and runs unit tests against distribution jar(s). Use .antrc or the
+ command line to control the jdk used to execute this build file.
+
+ Assumes that the distribution jar to be tested is in the base directory.
+ Use the "jardir" property to specify the path to the directory containing
+ the jar. Any other jars in this directory will also be added to the
+ classpath.
+
+ The default target, "test," executes clean as a dependency.
+-->
+
+<project default="test" name="commons-math" basedir=".">
+ <!-- JDK level -->
+ <property name="compile.source" value="1.5"/>
+ <property name="compile.target" value="1.5"/>
+
+ <property name="defaulttargetdir" value="target"/>
+ <property name="testclassesdir" value="target/test-classes"/>
+ <property name="testreportdir" value="target/test-reports"/>
+ <property name="jardir" value="${basedir}"/>
+ <path id="build.classpath">
+ <fileset dir="${jardir}">
+ <include name="*.jar">
+ </include>
+ </fileset>
+ </path>
+
+ <target name="clean" description="o Clean up the generated directories">
+ <delete dir="${defaulttargetdir}"/>
+ </target>
+
+ <target name="init" description="o Initializes some properties">
+ <echo>
+ JAVA ENVIRONMENT
+ **************************Java runtime version*****************************
+ ${java.runtime.version}
+ **************************Java class path**********************************
+ ${java.class.path}
+ **************************Java home****************************************
+ ${java.home}
+ *************************Java library path*******************************
+ Java library path: ${java.library.path}
+ ===========================================================================
+ </echo>
+ <!--Test if JUNIT is present in ANT classpath-->
+ <available property="Junit.present" classname="junit.framework.Test">
+ </available>
+ </target>
+
+ <target name="test" description="o Run the test cases" if="test.failure" depends="internal-test">
+ <fail message="There were test failures.">
+ </fail>
+ </target>
+
+ <target name="internal-test" if="Junit.present" depends="clean,junit-present,compile-tests,test-only"/>
+
+ <target name="test-only">
+ <mkdir dir="${testreportdir}"/>
+ <junit dir="./" failureproperty="test.failure" printSummary="yes" fork="true" haltonerror="false">
+ <sysproperty key="basedir" value="."/>
+ <formatter usefile="true" type="brief"/>
+ <classpath>
+ <path refid="build.classpath"/>
+ <pathelement path="${testclassesdir}"/>
+ </classpath>
+ <batchtest todir="${testreportdir}">
+ <fileset dir="src/test/java">
+ <include name="**/*Test.java"/>
+ <include name="**/*TestBinary.java"/>
+ <include name="**/*TestPermutations.java"/>
+ <exclude name="**/*AbstractTest.java"/>
+ </fileset>
+ </batchtest>
+ </junit>
+ </target>
+
+ <target name="junit-present" unless="Junit.present" depends="init">
+ <echo>
+ ================================= WARNING ================================
+ Junit isn't present in your ${ANT_HOME}/lib directory. Tests not executed.
+ ==========================================================================
+ </echo>
+ </target>
+
+ <target name="compile-tests" if="Junit.present" depends="junit-present">
+ <mkdir dir="${testclassesdir}"/>
+ <!-- includeantruntime="true" because JUnit may be provided by Ant -->
+ <javac destdir="${testclassesdir}"
+ deprecation="false"
+ debug="true"
+ includeantruntime="true"
+ source="${compile.source}"
+ target="${compile.target}"
+ optimize="false"
+ excludes="**/package.html">
+ <src>
+ <pathelement location="src/test"/>
+ </src>
+ <classpath>
+ <path refid="build.classpath"/>
+ </classpath>
+ </javac>
+ <copy todir="${testclassesdir}">
+ <fileset dir="src/test/resources">
+ <include name="**/*.xml"/>
+ <include name="**/*.txt"/>
+ </fileset>
+ </copy>
+ </target>
+</project>
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/commons-math.git
More information about the pkg-java-commits
mailing list